The App Router introduces a new mental model for rendering. Understanding when to use each strategy is essential for performance and developer experience.
Server Components (Default)
Every component in the App Router is a Server Component by default. They render on the server and send HTML to the client.
Key rule: Client Components can import other Client Components, but they cannot import a Server Component directly. Instead, pass Server Components as children or other props.
Server Actions
Functions that run on the server but can be called from Client Components.
Use when:
Form submissions
Data mutations (create, update, delete)
Any operation needing server-side resources triggered by user action
// app/actions.ts"use server";export async function createPost(formData: FormData) { const title = formData.get("title") as string; await db.post.create({ data: { title } }); revalidatePath("/posts");}
Decision Tree
Does it need interactivity or hooks? → Client Component
Does it fetch data or access server resources? → Server Component
Is it a form submission or mutation? → Server Action
Is it a leaf component with no data needs? → Server Component (smaller bundle)
Does it use a client-only library? → Client Component
Composition Pattern
Keep the Client Component boundary as small as possible:
// Server Component (page)export default async function Page() { const data = await fetchData(); return ( <section> <h1>{data.title}</h1> <InteractiveWidget initialData={data.items} /> </section> );}
This fetches data on the server but delegates interactivity to a small Client Component.