The Serialization Wall: RSC Props
hard⏱ 20 minreactlesson
An orders page (Server Component) renders a client-side <OrderTable> — and hands it three props that cannot survive serialization: a callback (onSelect), Date instances and a Map. The single-process preview happily renders all of it, which is exactly what makes this bug treacherous: in a real App Router build the function prop throws "Functions cannot be passed directly to Client Components", and the Date/Map props stop being Dates and Maps on the other side.
Your tasks:
- TODO(A) — Tear down the function prop: behavior can't be serialized. Move the selection interaction into the client module:
useState for the selected row id, onClick on the row, feedback in the caption and a highlight class.
- TODO(B) — Dates become strings at the wall: ship
placedAt as an ISO string from the page; the table re-creates a Date locally to format it.
- TODO(C) — The Map becomes plain data: replace the status-label
Map with a plain object and index into it on the client.
The mental model: the server → client boundary is a serialization wall — only JSON-ish data (plus a few React-blessed extras like promises and JSX) crosses it. Functions, class instances, Date, Map and Set do not.
Why this happens
Props from a Server Component to a Client Component travel through the RSC payload — a wire format, roughly JSON with a few React extensions. That's why the rules are what they are: functions are behavior, not data, so they can't cross (the escape hatches are moving the behavior into the client component, or passing a Server Action created with "use server"); Date/Map/Set are live objects with methods, so they arrive as something else — or fail the build outright. The reliable currency is plain data: objects, arrays, strings, numbers, booleans, null. Two idioms follow. First, state that exists purely for interaction (which row is selected) belongs in the client component that uses it — the server has no business owning it. Second, rich types get flattened at the wall (Date → ISO string, Map → plain object) and rehydrated on the client side. And note the trap this challenge is built on: a single-process dev preview cannot catch these bugs — only the real serialization boundary does.