Error: Only plain objects, and a few built-ins, can be passed to Client Components from Server Components. Classes or null prototypes are not supported.
ID: nextjs/rsc-serialization-error
Version Compatibility
| Version | Status | Introduced | Deprecated | Notes |
|---|---|---|---|---|
| 14 | active | — | — | — |
Root Cause
Data passed from a Server Component to a Client Component via props must be serializable over the RSC wire protocol. Non-serializable values include class instances, functions, Dates, Maps, Sets, Symbols, and objects with null prototypes (e.g., from Object.create(null) or some ORM results).
genericWorkarounds
-
93% success Convert class instances and ORM models to plain objects before passing as props
// Server Component import { prisma } from '@/lib/db'; import { ClientList } from './ClientList'; export default async function Page() { const users = await prisma.user.findMany(); // Convert Prisma models to plain objects: const plainUsers = users.map(u => ({ id: u.id, name: u.name, email: u.email, createdAt: u.createdAt.toISOString(), })); return <ClientList users={plainUsers} />; }Sources: https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns
-
88% success Move the data transformation logic to the Client Component and pass only serializable IDs or primitives
// Server Component export default async function Page() { const userIds = await getUserIds(); // Returns string[] return <UserDashboard userIds={userIds} />; } // ClientComponent.tsx 'use client'; export function UserDashboard({ userIds }: { userIds: string[] }) { // Fetch full user data client-side using SWR, React Query, etc. const { data } = useSWR(`/api/users?ids=${userIds.join(',')}`); } -
91% success Create a serialization utility that converts non-serializable types to RSC-safe plain objects
// lib/serialize.ts export function serialize<T extends Record<string, any>>(obj: T): Record<string, any> { return Object.fromEntries( Object.entries(obj).map(([key, val]) => [ key, val instanceof Date ? val.toISOString() : val instanceof Map ? Object.fromEntries(val) : val instanceof Set ? [...val] : typeof val === 'object' && val !== null ? serialize(val) : typeof val === 'function' ? undefined : val ]).filter(([_, v]) => v !== undefined) ); } // Usage in Server Component: const safeData = serialize(complexData); return <ClientComponent data={safeData} />;Sources: https://react.dev/reference/rsc/use-client#serializable-types
Dead Ends
Common approaches that don't work:
-
Use JSON.stringify/JSON.parse to serialize the data before passing as props
60% fail
While this can work for simple cases, it strips type information, loses Date objects (they become strings), silently drops undefined values, and fails on circular references. It's a fragile workaround that introduces subtle bugs and breaks TypeScript type safety.
-
Pass the entire class instance or ORM model object directly as a prop
98% fail
Class instances have prototypes and methods that cannot cross the Server/Client boundary. The RSC protocol only supports plain objects, strings, numbers, booleans, null, arrays, and a few built-in types like Date (as of newer React versions).
-
Wrap the Client Component in a Server Component that converts data to a string
75% fail
Converting structured data to a string loses all typing and structure. The Client Component then needs to re-parse it, which is error-prone and defeats the purpose of typed props.