Next.js: Objects with toJSON methods are not supported while passing props to Client components

365 Views Asked by At

I am fetching some data from MySQL database using Sequelize ORM in Next.js App router. This is what my method "getTransactions" returns:

const { rows, count } =  await Transaction.findAndCountAll({
    limit,
    offset,
    where: whereClause,
    order: [['created_at', 'DESC']],
});

return {
    items: rows,
    totalItems: count,
    totalPages: Math.ceil(count / limit),
    currentBalance,
};

While passing this data to client component, I get the following warning in the console:

Warning: Only plain objects can be passed to Client Components from Server Components. Objects with toJSON methods are not supported. Convert it manually to a simple value before passing it to props.

Server Component:

const data = await getTransactions({ page, limit, query: search });
return (
    <TransactionsTable response={data} />
);

Client Component:

const TransactionsTable = ({ response }: { response : PaginatedTransactions | null }) => {
    //TSX
}

I tried to serialize and deserialize the data and made sure the timestamps (created_at and updated_at) are converted to Dates before sending to the client component. Now, it works fine and no warning is logged to the console. But I do not get the point of doing this additional stuff.

const data = await getTransactions({ page, limit, query: search });
const serialize = JSON.stringify(data);
const deserialize = JSON.parse(serialize, (key, value) => {
    if (key === "created_at" || key === "updated_at") {
        return new Date(value);
    }
    return value;
}) as PaginatedTransactions | null;

<TransactionsTable response={deserialize} />
1

There are 1 best solutions below

2
On BEST ANSWER

The reason you encountered this error is because only following things can be passed down to a client component and vice versa:

  • Plain objects and promises
  • Strings
  • Numbers
  • Booleans

The values you are simply "passing" to the client component from your perspective are in reality sent over the network and thus need to be serializable.

// this does not work
export default async function ServerComponent() {
  const response = await fetch("..");
  return <ClientComponent data={response} />;
}

// this does work
export default function ServerComponent() {
  const promise = fetch("..");
  return <ClientComponent data={promise} />;
}

// this does work too
export default async function ServerComponent() {
  const json = await (await fetch("..")).json();
  return <ClientComponent data={json} />;
}

An exception to this paradigm are server actions that can be passed to client components (even if a kind of function too) but similarly to the previously mentioned behavior they only can accept the same type of values as function parameters that client components can via their props.