React Server Components flip the default: your components run on the server unless you explicitly opt into the client with 'use client'. This means you can query databases, read files, and use heavy libraries in your components without shipping a single byte to the browser. The tradeoff is a new mental model around serialization boundaries, composition rules, and knowing exactly where your code executes.
Master react server components
Take the React Server Components course with hands-on lessons and challenges.
This component queries Prisma directly with a join across three tables. The entire function, including the db import and query, stays on the server. The browser receives only the rendered HTML. No API route, no fetch call, no loading state management.
The page itself is a Server Component that fetches data. Only the interactive EnrollButton is a Client Component. This keeps the client bundle minimal — the course description, section list, and database query code all stay on the server.
The Server Action validates input with Zod, authenticates the user, updates the database, and revalidates the cache — all in one function. No API route to maintain. The form works with progressive enhancement: it submits even before JavaScript loads.
Each Suspense boundary defines an independent streaming chunk. The dashboard shell renders immediately with skeleton placeholders. LearningProgress might resolve in 100ms, while the Leaderboard query takes 400ms — but neither blocks the other. Users see content progressively instead of staring at a blank page.
The Providers component is a Client Component (it uses hooks and context), but its children can still be Server Components. The RootLayout renders Sidebar on the server, and the serialized output gets passed through Providers. This is the standard pattern for wrapping your app with context providers without forcing the entire tree to be client-side.
The bookmark toggles instantly in the UI via useOptimistic while the Server Action runs in the background. If the action fails, React automatically rolls back to the real state. This pattern gives users instant feedback without sacrificing data consistency.
Adding `'use client'` at the top of a page component because it needs one interactive element — this pulls the entire page and all its imports into the client bundle
Extract only the interactive part into a separate Client Component. Keep the page as a Server Component and render the Client Component as a child. A page with a like button doesn't need to be a Client Component — only the button does.
Trying to import a Server Component inside a Client Component — e.g. `import ServerChart from './ServerChart'` inside a `'use client'` file
Pass the Server Component as `children` or a prop from a Server Component parent. The server renders it first and sends the serialized output to the Client Component. This is the composition pattern, not an import.
Passing non-serializable values (functions, class instances, Dates, Maps) as props from a Server Component to a Client Component
Only serializable data crosses the server/client boundary: strings, numbers, booleans, plain objects, arrays, null, and a few React types. Convert Dates to ISO strings, Maps to plain objects, and move callback functions into Client Components or use Server Actions.
Trusting closure values in Server Actions for authorization — e.g. closing over a `userId` prop and using it to delete a record without re-verifying on the server
Closure values in Server Actions are serialized, sent to the client, and sent back. A malicious user can tamper with them. Always re-authenticate inside the action: call `auth()` or `getSession()` to get the current user's identity from the server.
React Server Components run on the server by default and ship zero JavaScript to the browser. Add `'use client'` only when a component needs hooks, event handlers, or browser APIs. Async Server Components fetch data directly with `await` — no useEffect, no API routes. Server Actions handle mutations through `'use server'` functions that you can call from forms and event handlers. Suspense boundaries enable streaming, so each section of a page loads independently. The key discipline is keeping the client boundary as small as possible and understanding what can cross the serialization boundary between server and client.
Interactive lessons and challenges on Stanza, practice in VS Code, Cursor, or the web.
Interactive lessons and challenges, right in your code editor.
Check the free courses. No credit card.