| name | component-design |
| description | Design React/Next.js component APIs with clear Server/Client boundaries, composable patterns, and proper seam placement. Use when designing new components, refactoring existing ones, or deciding between Server/Client Component boundaries. |
Component Design (Next.js)
Guiding principles for designing React components in Next.js App Router โ focusing on API surface, composition, and the Server/Client boundary.
Core Principles
1. Server Component by Default
Prefer Server Components unless you need interactivity (hooks, event handlers, browser APIs). Every component that can be a Server Component should be one.
app/page.tsx โ Server Component (data fetching, layout)
app/page.tsx โ Client Component only if interactive
components/button.tsx โ Client Component (has state/events)
2. Push Client Boundaries Down
When you need a Client Component, make it as thin as possible. Wrap the interactive part in a Client boundary and keep everything else on the server.
import { Comments } from './comments'
import { CommentList } from './comment-list'
export default function Page() {
return (
<div>
<CommentList /> {/* Server: data fetching, rendering */}
<Comments /> {/* Client: interactivity only */}
</div>
)
}
3. Interface First
Before writing implementation, define the component's public interface:
- Props (required vs optional, types)
- Children composition model (slots, render props, compound components)
- Ref forwarding strategy
- Loading, empty, error states
4. Test Through the Interface
If testing requires reaching into internal state or private methods, the interface is wrong. The public API should be the only test surface.
Server/Client Component Decision Tree
Does the component need hooks, events, or browser APIs?
โโ No โ Server Component โ
โโ Yes โ Client Component
โโ Can the interactive part be isolated?
โ โโ Yes โ Extract into a thin Client wrapper, keep rest Server โ
โ โโ No โ Make the whole thing Client, but keep it focused
โโ Do you need to pass Server data to it?
โโ Yes โ Pass as props from the Server parent
โโ No โ Fetch inside the Client Component
Seam Placement
A seam is where you can alter behavior without editing in that place. Good seams in Next.js:
| Seam | Purpose |
|---|
layout.tsx | Persistent shell, shared UI across routes |
loading.tsx | Streaming fallback per route segment |
error.tsx | Error boundary per route segment |
not-found.tsx | 404 boundary per route segment |
| Component props | Polymorphism via different prop combinations |
Slot props (children, named slots) | Composition over configuration |
Checklist