Course outline · 0% complete

0/29 lessons0%

Course overview →

Props across the boundary

lesson 3-4 · ~10 min · 10/29

Props that travel over a wire

There is one more rule of the server/client split, and it bites real teams weekly. When a server component renders a client component, the props it passes do not stay inside one program — the server serializes them (writes them down as data), sends them to the browser as part of the page payload, and React rebuilds them there. So props crossing the server → client boundary must be serializable: strings, numbers, booleans, null, and plain arrays and objects made of those.

What cannot be written down and rebuilt: functions, class instances, database handles. Pass a function from a server component to a client component and Next.js stops you with the error every Next.js developer eventually meets:

Error: Functions cannot be passed directly to Client Components

Without this rule the framework would have to ship arbitrary server code to the browser — exactly what server components exist to prevent.

Worked example: the broken delete button

A server component tries to hand its client child a callback:

// app/posts/page.js  (server)
import DeleteButton from "./delete-button"; // "use client"

export default async function Posts() {
  const posts = await getPosts();
  return posts.map((post) => (
    <DeleteButton
      key={post.id}
      onDelete={() => db.posts.remove(post.id)}  // ✗ function prop — build error
    />
  ));
}

The fix follows from the rule: send data across the boundary and keep behavior on the side where it runs.

// server: pass the id (a string — serializable)
<DeleteButton key={post.id} postId={post.id} />

Inside DeleteButton, the click handler is defined locally and calls the server through a proper channel — a server action or an API route (both are Unit 6). One deliberate exception exists: a server action may be passed as a prop, because Next.js sends a secure reference to it, not the function's code.

Quiz

Which of these props can a server component legally pass to a "use client" component?

Code exercise · javascript

Build the checker Next.js effectively runs on your props (pure JavaScript, runnable here). Write canCrossBoundary(value): return true if the value could be serialized and rebuilt in the browser — strings, numbers, booleans, null, and arrays or plain objects whose contents all pass the same test. Return false for functions (and anything containing one).

Problem

A server component passes formatDate={(d) => d.toLocaleDateString()} to a "use client" component and the build fails. Following the data-across, behavior-local rule, should the fix move the formatting function into the client component, or serialize the function as a string? (answer: 'move' or 'serialize')