Default every component to a Server Component; add a client-component directive only to the specific leaf components that actually need interactivity, browser APIs, or React state/effects — pushing that directive to the top of a large tree drags everything beneath it into the client bundle unnecessarily.
Never import a server-only module (database client, secret-bearing SDK) into a file that's part of a client component's tree, even transitively — it either fails the build or, worse, gets bundled and shipped to the browser.
Data fetching
Fetch data directly in Server Components (async function components with await) rather than client-side effect-plus-fetch for anything that can be known at render time — it removes a client-side loading waterfall and keeps secrets/queries server-side.
Use route-level loading and error boundaries for granular, streaming-friendly loading and error UI instead of manual boolean state for every data-fetching component.
Environment variables
Only variables explicitly prefixed for client exposure (per the framework's convention) are safe in client-side code — every other variable is server-only by default and must stay that way; this specializes the general secrets-hygiene rule to Next.js's specific bundling behavior.
Validate required environment variables at startup (a small schema check run once) rather than discovering a missing variable via a runtime crash deep in a request handler.
File and folder structure
Colocate a route's own components, tests, and styles within its route segment folder; keep only genuinely shared, cross-route code in a top-level shared directory — this keeps "what does this route depend on" locally answerable.
Use route groups to organize routes without affecting the URL structure, rather than deeply nested folders that don't reflect the actual URL.
Server actions and mutations
Validate all input to a Server Action server-side (never trust that client-side form validation ran) — a Server Action is a public API endpoint with a different calling convention, not a trusted RPC.
Keep Server Actions focused (one mutation, one action) rather than a single catch-all action branching on a "type" parameter — it keeps authorization checks per-action instead of a branch that's easy to under-guard.
Metadata and rendering
Use the framework's typed metadata API per route rather than manually constructed head tags, so metadata stays server-rendered and type-checked.
Default to static/server rendering; opt into dynamic rendering deliberately (and know why) rather than accidentally forcing every route dynamic through an unnoticed uncached data call.
Comments (0)
Sign in to comment. Sign in
No comments yet. Be the first to add one.