Only plain objects can be passed to Client Components from a Server Component, so a class instance, an ORM row, or any other object with a prototype fails with this exact message, either during the build's prerender step or the first time the route renders.
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.Next.js also prints the specific object or prop it choked on, usually naming the property key and a short description of the value's shape, right after the message above. This error means a Server Component tried to hand a value with a prototype down to a Client Component as a prop, and React had no way to convert that prototype into serialized data.
The smallest reproduction
This Server Component defines a small class, creates an instance of it, and passes that instance straight into a Client Component as a prop named customer.
// app/customers/[id]/page.tsx
import CustomerCard from '@/app/ui/customer-card'
class Customer {
constructor(public name: string, public email: string) {}
}
export default function CustomerPage() {
const customer = new Customer('Ava Chen', 'ava@example.com')
return <CustomerCard customer={customer} />
}The card component below marks itself as a Client Component because it needs local state to toggle an edit form, and it just reads whatever object it receives through the customer prop.
// app/ui/customer-card.tsx
'use client'
import { useState } from 'react'
export default function CustomerCard({
customer,
}: {
customer: { name: string; email: string }
}) {
const [editing, setEditing] = useState(false)
return <button onClick={() => setEditing(!editing)}>{customer.name}</button>
}Rendering CustomerPage throws the error at the top of this article. Both fields on the customer object are plain strings, so the data itself is not the problem. The Customer instance carries a prototype from its class, and React refuses to serialize that prototype into the payload sent to the browser.
Why this happens
A Client Component's props travel to the browser inside the React Server Component payload, a serialized tree built from every Server Component above it. React only knows how to reconstruct plain data shapes from that payload, such as plain objects, arrays, strings, numbers, and a short list of built-ins like Date, Map, and Set.
A class instance is not a plain object even when its own fields look like ordinary data, because it also carries a reference to its class's prototype, which can include methods, getters, and inherited behavior. React cannot rebuild that prototype in the browser, so it rejects the value outright instead of sending a broken, method-less copy.
For the complete list of which prop types serialize and which do not, see What Props Can You Pass from Server to Client Components in Next.js. This article only covers the class instance and non-plain-object case and its fix.
The fix: convert the value to a plain object
Map the class instance to a plain object literal that contains only the fields the Client Component actually needs, and pass that instead of the instance itself.
// app/customers/[id]/page.tsx
import CustomerCard from '@/app/ui/customer-card'
class Customer {
constructor(public name: string, public email: string) {}
}
export default function CustomerPage() {
const customer = new Customer('Ava Chen', 'ava@example.com')
const plainCustomer = { name: customer.name, email: customer.email }
return <CustomerCard customer={plainCustomer} />
}plainCustomer is an object literal, so it has the default Object.prototype and nothing else attached to it. React can serialize that shape without any trouble, and the Client Component's own type already describes exactly the fields it expects.
For a repeated shape, write the mapping once as a small function next to where the data is fetched, instead of inline in every component that touches it.
// lib/customers.ts
class Customer {
constructor(public name: string, public email: string) {}
}
export function toPlainCustomer(customer: Customer) {
return { name: customer.name, email: customer.email }
}Call toPlainCustomer right after you construct or receive the instance, before it ever reaches a Client Component's props. This keeps the conversion in one place instead of scattering field picks across the codebase.
How to confirm the error is gone
Reload the page or rerun the build and the error text should no longer appear in the terminal or the browser overlay. Open the customer card and confirm it still renders the expected name, which shows the plain object carried the same data the class instance held.
Where this error actually comes from in real projects
Most readers never write new SomeClass() directly inside a Server Component. The instance usually comes from a library instead, and the fix is the same: convert the result to a plain object before it reaches a Client Component.
| Common source | What it returns |
|---|---|
| ORM query results | Model instances or row classes, not plain objects |
| Database driver documents | Wrapper objects with methods like toObject |
| Date or money utility libraries | Wrapper classes around a primitive value |
Check any object that comes back from a database call, an ORM, or a third-party SDK before passing it to a Client Component, since these are the values most likely to carry a prototype without looking like it in your own code. For more on fetching data directly inside a Server Component, see Fetching Data Directly from a Database in a Server Component, and for the function-shaped version of this same class of error, see Fixing "Functions Cannot Be Passed Directly to Client Components".
Rune AI
Key Insights
- The error means a Server Component passed a class instance or other non-plain object as a prop to a Client Component.
- A class instance carries a prototype and methods that React cannot serialize, even when its fields look like plain data.
- The fix is to convert the value into a plain object with only the fields the Client Component actually needs.
- ORM rows, database driver documents, and wrapper objects for dates or money are the most common real-world source.
- Adding a toJSON method to the class does not fix this, because React's serialization does not call it.
- Do the conversion once, close to where the data is fetched, instead of inside every component that receives it.
Frequently Asked Questions
Why does a class instance fail when its fields look like plain data?
Does this error only happen with custom classes I write myself?
Can I fix this by adding a toJSON method to the class?
Does JSON.parse(JSON.stringify(value)) fix this safely?
Conclusion
This error means a Server Component tried to pass a value with a prototype, such as a class instance or an ORM row, into a Client Component's props. Map the value to a plain object with only the fields the Client Component needs before it crosses the boundary, and the error goes away because React only ever sees a plain data shape.
More in this topic
`generateMetadata` Explained with Real Examples
What generateMetadata does, when it runs, and how to use it for real routes: awaited params, deduplicated data fetching, extending parent metadata, and returning a 404 from metadata.
Canonical URLs in Next.js: `metadataBase`, `alternates.canonical`, and Dynamic Pages
How canonical URLs work in the Next.js App Router: setting metadataBase once, writing alternates.canonical per route, handling dynamic segments, and what happens when the base URL is missing.
Open Graph and Twitter Card Metadata in Next.js
How to write Open Graph and Twitter card metadata in the Next.js App Router: the openGraph and twitter fields, automatic card defaults, article tags, and image merge rules.