Course outline

Next.js / Basic: routes and execution boundaries / Lesson 3

Server/client boundaries and serializable props

Keep browser interaction small and pass only approved data across the React server/client boundary.

About 4 min readContent reviewed
On this page

Lesson goals

  • Explain use client as a module-graph boundary rather than an authorization setting.
  • Review dependencies and props for secrets, unsupported values and unnecessary browser code.

A client entry brings its imported dependency graph

The use client directive marks a module entry whose component can use client features. Modules imported through that client graph must be suitable for the browser bundle. Putting the directive high in a layout can therefore ship far more code than the one interactive button needs. Keep server-only source access behind a clear module boundary and compose small client controls into server-rendered UI. Passing server-rendered content as children is different from importing a server data module into a client file. Review the actual import graph rather than assuming a file name or folder alone protects implementation.

Expose a narrow interactive component

The fragment is a Client Component with a local counter and an ordinary numeric prop. Its useState import and JSX compilation require React/Next integration and were not executed. The server parent should pass only the initial count intended for this user, not an entire account or persistence object. Client state is presentation state, not authoritative permission or a durable count. A user can alter client code, so a button becoming enabled cannot authorize a server operation. Keep the actual mutation checks on the server even when the client prevents an obvious invalid click.

tsx Illustrative small client boundary
"use client";
import {useState} from "react";
export function Counter({initial}:{initial:number}) {
  const [count,setCount]=useState(initial);
  return <button onClick={() => setCount(c => c+1)}>{count}</button>;
}

The state is local presentation state. No React render, event or hydration was executed.

React serialization is a contract, not JSON alone

Props crossing the server/client boundary must use values supported by React’s serialization contract. That contract is richer than simply JSON, but ordinary arbitrary functions and source handles should not be passed as data. Server Function references are a separately defined mechanism, not a reason to send any callback. Prefer a small deliberate view model with stable IDs and domain values. Do not pass access tokens, database records with secret fields or internal errors just because a value can technically serialize. Safety requires an approved audience and meaning in addition to a supported representation.

Test the selected prop surface independently

The local factory constructs a public label and bounded count from already eligible synthetic data. It deliberately does not copy the source object. It is a plain JavaScript model, not a complete React serializability detector. Real React supports several value types beyond these simple fields, so do not advertise this narrow model as the framework’s full rule. The important test is that the source token never enters the prop object. An actual build test should also verify that a server-only dependency cannot be imported into the client graph accidentally.

javascript Complete offline prop projection
function counterProps(record) {return {label:record.title,initial:Math.min(record.publicCount,99)};}
const props=counterProps({title:"Courses",publicCount:120,sourceToken:"private-lab-token"});
console.assert(props.initial === 99 && props.label === "Courses");
console.assert(!Object.hasOwn(props,"sourceToken"));

Only label and initial are selected. This does not test the React payload or authorize the source data.

Handle browser-only work without breaking initial output

Browser APIs such as localStorage and window are unavailable during server evaluation. Isolate them in the proper client lifecycle rather than branching initial render on window existence and producing mismatched HTML. Effects run after commit and should manage an external synchronization need, not duplicate server data fetching without a reason. Keep cleanup explicit for listeners and subscriptions. This course runs only the prop factory; client bundling, browser APIs, effects and hydration remain unexecuted. A real isolated component and browser test must verify the selected Next/React integration and its actual lifecycle.

Common errors and fixes

A client import includes a private source module

Why it happens
The client module graph crossed the server-only boundary.
How to fix it
Separate data access and pass a narrow approved view model.
Prevent it next time
Use build boundary checks and dependency review.

Any serializable secret is sent as a prop

Why it happens
Technical serializability was mistaken for permitted disclosure.
How to fix it
Apply audience policy and explicit projection.
Prevent it next time
Assert confidential fields are absent.

window access fails during rendering

Why it happens
Client designation was assumed to prevent server evaluation.
How to fix it
Move browser integration to an appropriate lifecycle.
Prevent it next time
Verify initial HTML and hydrated output.

Check your understanding

1. Is React’s prop boundary identical to JSON.stringify support?

Show worked solution

No. React defines its own supported serialization values and Server Function reference behavior. Keep a narrow intentional data model and consult that contract. A simple JSON-only convention may be useful locally, but it should not be presented as the complete React rule or as authorization to disclose every supported value.

2. Why should the server recheck an action when the client disables its button?

Show worked solution

Client UI is controlled by the caller and can be bypassed. It improves interaction but cannot establish current identity, object eligibility or transaction preconditions. Validate and authorize inside the actual server entry point, then return a canonical result for the client to reconcile.

Key takeaways

  • use client defines an import boundary and interactive capability.
  • Supported serialization does not establish permitted disclosure.

Official references