nextjs serialization_error ai_generated true

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

Also available as: JSON · Markdown
90%Fix Rate
91%Confidence
80Evidence
2023-01-01First Seen

Version Compatibility

VersionStatusIntroducedDeprecatedNotes
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).

generic

Workarounds

  1. 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

  2. 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(',')}`);
    }

    Sources: https://nextjs.org/docs/app/building-your-application/data-fetching/fetching#fetching-data-on-the-client

  3. 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:

  1. 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.

  2. 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).

  3. 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.

Error Chain

Leads to:
Preceded by:
Frequently confused with: