Next.js only allows serializable props to cross from a Server Component to a Client Component, meaning React must be able to convert the value into the React Server Component payload and rebuild it in the browser. Strings, numbers, plain objects, arrays, Date, Map, Set, and Promises are all allowed. Functions, class instances, and Symbols are not, with one exception for Server Actions.
// app/product/[id]/page.tsx
import ProductCard from '@/app/ui/product-card'
import { getProduct } from '@/lib/data'
export default async function ProductPage({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
const product = await getProduct(id)
return <ProductCard product={product} />
}This page runs on the server and fetches a plain object with a name, a price, and an expiry field that is a Date. All of that survives the trip into the Client Component below it, because every value inside the object is serializable.
Why the constraint exists
Next.js renders Server Components into the React Server Component payload, a serialized tree that also carries any props destined for a Client Component. That payload has to travel from the server process to the browser, so it cannot contain a live function reference, an open database connection, or a class instance with private internal state. React only serializes values it knows how to reconstruct on the other side.
This is a React constraint, not a Next.js-specific rule, because it comes from how React Server Components serialize data for the client. It applies the same way in any framework built on RSC. For the mechanics of the boundary itself, see Server Components vs Client Components in Next.js.
What you can and cannot pass
| Prop type | Allowed |
|---|---|
| String, number, boolean, null, undefined | Yes |
| Plain object with serializable properties | Yes |
| Array of serializable values | Yes |
| Date, Map, Set | Yes |
| TypedArray, ArrayBuffer | Yes |
| Promise, for streaming with the use API | Yes |
| Function or event handler | No |
| Server Action ("use server" function) | Yes, as a special case |
| Class instance (other than the built-ins above) | No |
| Symbol not registered globally | No |
A plain object only qualifies if every value inside it also qualifies. An object with a nested function or a nested class instance fails the same way a top-level one would.
Functions are the most common mistake
Passing a plain function as a prop is the failure readers hit most often, usually when trying to hand a click handler down from a Server Component into a button below it.
// app/ui/add-to-cart-button.tsx
'use client'
export default function AddToCartButton({
onAddToCart,
}: {
onAddToCart: () => void
}) {
return <button onClick={onAddToCart}>Add to cart</button>
}The component above marks itself as a Client Component with the directive on its first line, because it responds to a click event. The problem is not this file. It is whatever Server Component tries to define onAddToCart as a plain function and pass it in as a prop, since a plain function only exists in server memory and cannot travel through the RSC payload.
Error: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server".There are two real fixes. Move the click behavior into the Client Component itself using local state, or pass a genuine Server Action marked with "use server", which Next.js compiles into a callable reference instead of a raw function. See Fixing "Functions Cannot Be Passed Directly to Client Components" for the full walkthrough of both fixes.
Passing a Server Component as children is different
Handing a Server Component to a Client Component through children or another prop is a separate pattern from passing data, and it does not hit the serialization rule at all. The Server Component renders on the server first, and only its finished output crosses the boundary, not a live reference to the component itself.
// app/page.tsx
import Modal from './ui/modal'
import Cart from './ui/cart'
export default function Page() {
return (
<Modal>
<Cart />
</Modal>
)
}The cart component still runs entirely on the server even though it visually ends up inside the modal, a Client Component. This is composition, not prop serialization, so readers who conflate the two often try to fix a children slot the same way they would fix a broken prop, when nothing about it is actually broken. See Passing Server Components as Children to Client Components for the full pattern and how far it can be nested.
A practical use case
A dashboard page fetches an order with a total, a status, and a timestamp for when it was placed, then passes the whole object to a Client Component that formats the date for the reader's own locale and lets them mark the order as reviewed.
// app/orders/[id]/page.tsx
import OrderStatus from '@/app/ui/order-status'
import { getOrder } from '@/lib/data'
export default async function OrderPage({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
const order = await getOrder(id)
return <OrderStatus order={order} />
}The order is a plain object containing a string, a number, and a Date, so the whole thing crosses the boundary intact and needs no manual conversion. Inside the Client Component, that Date can be formatted with the reader's own locale and timezone in the browser, something a Server Component cannot do reliably because it does not know where the reader is located.
Common confusion
TypeScript does not reliably stop a non-serializable prop from compiling. A class instance or a plain function looks like any other typed value to TypeScript, so nothing about the type signature warns you. The error only appears at build or render time, once React actually tries to serialize the value and finds something it cannot convert.
Treat this as a runtime concern to verify by testing the actual render, not something a passing type check has already confirmed for you. For more on how values move across the boundary, including how a Promise prop can stream in with Suspense, see How to Pass Props Across the Server Client Boundary.
Rune AI
Key Insights
- Props from a Server Component to a Client Component travel through the RSC payload and must be serializable by React.
- Strings, numbers, booleans, null, undefined, plain objects, arrays, Date, Map, Set, TypedArrays, and Promises are all allowed.
- Functions, class instances, and unregistered Symbols are not allowed as props.
- A function marked with "use server" is the one exception, because Next.js compiles it into a callable reference.
- Passing a Server Component as children is a different pattern and does not need to be serializable.
- TypeScript does not reliably catch this. The error only shows up at runtime.
Frequently Asked Questions
Can I pass a Date object as a prop to a Client Component?
Why do functions fail as props but Server Actions do not?
Does TypeScript catch a non-serializable prop at compile time?
Is passing a Server Component as children the same as passing serializable props?
Conclusion
A prop crossing from a Server Component to a Client Component has to survive the trip through the RSC payload, so React only allows values it knows how to serialize. Strings, numbers, plain objects, arrays, Date, Map, Set, and Promises all make it through, while functions, class instances, and Symbols do not, unless the function is a Server Action.
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.