What Props Can You Pass from Server to Client Components in Next.js?

Props that cross from a Server Component to a Client Component must be serializable by React. Learn exactly which prop types are allowed, which ones fail, and why.

7 min read

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.tsxApp.tsx
// 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 typeAllowed
String, number, boolean, null, undefinedYes
Plain object with serializable propertiesYes
Array of serializable valuesYes
Date, Map, SetYes
TypedArray, ArrayBufferYes
Promise, for streaming with the use APIYes
Function or event handlerNo
Server Action ("use server" function)Yes, as a special case
Class instance (other than the built-ins above)No
Symbol not registered globallyNo

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.tsxApp.tsx
// 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.

texttext
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.tsxApp.tsx
// 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.tsxApp.tsx
// 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

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.
RunePowered by Rune AI

Frequently Asked Questions

Can I pass a Date object as a prop to a Client Component?

Yes. Date, Map, Set, TypedArrays, and Promises are all serializable and can be passed directly from a Server Component to a Client Component.

Why do functions fail as props but Server Actions do not?

A plain function only exists in server memory and cannot be sent over the RSC payload. A function marked with the "use server" directive is compiled into a callable reference the client can invoke, so it survives the boundary.

Does TypeScript catch a non-serializable prop at compile time?

Not reliably. TypeScript treats a class instance or function as a normal value unless you add stricter typing yourself. Next.js only reports the problem at runtime, once React tries to serialize the value.

Is passing a Server Component as children the same as passing serializable props?

No. Passing a Server Component as children or another prop sends its already-rendered output, not raw data, so it does not need to be serializable the way a plain value does.

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.