React Server Components & Server Actions

React Server Components (RSC) are components whose code runs on the server and never ships to the browser . They render once, on the server, and stream a description of the UI to the client — so they can read your database or filesystem directly, pull in heavy libraries with zero bundle cost, and hand finished data to small interactive Client Components marked with "use client" . Server Actions are the write side of the same model: async functions marked "use server" that you can wire straight to a <form> to mutate data without hand-building an API. By the end you'll know exactly which code runs where, why it makes apps faster, and how the Next.js App Router puts it all together.

Learn React Server Components & Server Actions in our free React course — a beginner-friendly interactive lesson with runnable examples, a practice exercise…

Part of the free React course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

What You'll Learn

1️⃣ Server vs Client: Where Code Runs

In a classic React app, every component ships to the browser. RSC splits that world in two. A Server Component runs once on the server and its source never reaches the client. A Client Component — anything that needs useState , useEffect , or an event handler — opts in with the "use client" directive at the top of the file.

The demo below models the resolver the framework runs over your component tree: walk it, and once you cross a "use client" marker, everything below it is client too. Run it and read the placements:

2️⃣ Why RSC Exist: Less JS, Direct Data

Two payoffs drive the whole design. First, less JavaScript : the code and libraries used only to fetch and shape data never go to the browser, so visitors download a fraction of what a fully client-rendered app would send. Second, direct data access : a Server Component can await your database, read the filesystem, or call an internal service with no client-side fetch , no loading spinner, and no extra round trip.

📊 The two worlds side by side

Concern

Client-fetched component

Server Component

Data fetching

useEffect + fetch

await db.query()

Loading state

manual isLoading

<Suspense> / loading.tsx

Bundle cost of libs

shipped to browser

zero — stays server-side

Secrets / API keys

risky to expose

safe on the server

Round trips to render

HTML → JS → fetch → data

data already in the HTML

3️⃣ Server Actions: Mutations Without an API

Reading data on the server is half the story. To write data you use a Server Action : an async function marked with the "use server" directive. The framework lets you call it from your components — most powerfully by passing it straight to a form's action prop. When the form submits, React serializes the inputs, runs your function on the server, and you mutate data and revalidate the UI with no hand-written fetch or route handler in sight.

The demo models the action lifecycle every Server Action follows — validate → mutate → revalidate — without needing a real database. Run it and watch an empty submission get rejected while a real one goes through:

4️⃣ How the Next.js App Router Uses RSC

The Next.js App Router (the app/ directory) is built on RSC from the ground up. Every file in it is a Server Component by default ; you reach for "use client" only at the interactive leaves. A few special files do a lot of work:

File

Role

page.tsx

The route's UI — a Server Component unless you opt out

layout.tsx

Shared shell that wraps child routes; persists across navigation

loading.tsx

Instant Suspense fallback while a Server Component awaits data

error.tsx

Error boundary for the segment (must be a Client Component)

actions.ts

A module of "use server" mutations for that feature

🎯 Your Turn: Decide Where Code Lives

Fill in the blanks so the rules match how React decides server vs client. The core instinct: interactivity needs the client; plain rendering can stay on the server.

Reorder Challenge

These lines of a Server Action handler are scrambled. Put them in the correct order:

Quick Recall

1) A file has no directive at the top in the App Router. Server or client?

Server Component — that's the default. You only become a Client Component by adding "use client" to the file (or importing a module that has it).

2) Your component needs onClick . What must you add?

Put "use client" at the very top of the file. Event handlers and state need the browser runtime, so that component must be a Client Component.

3) Why does moving data fetching to a Server Component shrink the bundle?

The fetching/parsing libraries and the component's data code run only on the server and are never sent to the browser. Visitors download only the small interactive pieces.

4) What three steps does a typical Server Action perform?

Validate the input, mutate the data on the server, then revalidate (e.g. revalidatePath ) so the affected UI re-renders with fresh data.

Common Errors (and the fix)

📋 Quick Reference

You want to…

Do this

Default a component to the server

add no directive (App Router)

Use state / effects / onClick

"use client" at top of file

Fetch data on the server

async function + await

Mutate data

"use server" action + <form action={fn}>

Refresh UI after a write

revalidatePath("/route")

Show a loading fallback

loading.tsx + <Suspense>

Frequently Asked Questions

Mini-Challenge: Tag Each Component

Finish classify so each component gets labeled "client" if it needs interactivity, otherwise "server" — the exact call you make on every component you write.

🎉 Lesson Complete

Practice quiz

What is the defining characteristic of a React Server Component (RSC)?

  • It always renders faster than a Client Component
  • It runs only after the page has hydrated
  • Its code runs on the server and never ships to the browser
  • It can use useState and useEffect freely

Answer: Its code runs on the server and never ships to the browser. An RSC runs once on the server, produces a serialized description of UI, and its source is never sent to the client.

In a framework built around RSC (like the Next.js App Router), what is the default for a component with no directive?

  • A Server Component
  • A Client Component
  • Neither until you choose
  • It depends on whether it uses JSX

Answer: A Server Component. Server is the default. A file becomes a Client Component only when it (or a module it imports for rendering) is marked "use client".

Which directive marks a file as a Client Component?

  • "use server"
  • "use strict"
  • "client only"
  • "use client"

Answer: "use client". "use client" at the top of a file opts that module and its render imports into the client. The boundary flows downward.

Can a Server Component use useState, useEffect, or onClick?

  • Yes, all of them
  • No — those need the browser runtime, so move them into a "use client" child
  • Only useEffect
  • Only when wrapped in Suspense

Answer: No — those need the browser runtime, so move them into a "use client" child. Server Components have no client runtime: no state, effects, or event handlers. Push interactivity down into a small Client Component.

Where should you place the "use client" directive to keep the most code on the server?

  • As low in the tree as possible, on the small interactive leaf
  • On the root layout so everything is consistent
  • On every file to be safe
  • On the page that fetches data

Answer: As low in the tree as possible, on the small interactive leaf. The boundary flows downward, so marking a high component drags its whole subtree to the client. Mark the smallest interactive leaf instead.

How does a Server Component typically fetch data?

  • useEffect + fetch with a loading flag
  • It calls an /api route from the browser
  • It awaits the data directly (e.g. await db.query()) since it's an async function on the server
  • It cannot fetch data at all

Answer: It awaits the data directly (e.g. await db.query()) since it's an async function on the server. A Server Component can be async and await the database or an internal service directly — no client fetch, no spinner, no extra round trip.

Why does moving data fetching into a Server Component shrink the client bundle?

  • React compresses server code more aggressively
  • The fetching/parsing libraries run only on the server and are never sent to the browser
  • It disables source maps
  • It removes the need for React on the client

Answer: The fetching/parsing libraries run only on the server and are never sent to the browser. The DB client, parsers, and SDKs used to shape data stay server-side. Visitors download only the small interactive pieces.

What is a Server Action?

  • A client-side event handler
  • A special kind of useEffect
  • A Next.js middleware file
  • An async function marked "use server" that runs on the server and can be bound straight to a <form action={fn}>

Answer: An async function marked "use server" that runs on the server and can be bound straight to a <form action={fn}>. Server Actions are the write side of RSC: "use server" functions you can call from components (often via a form's action prop) to mutate data without a hand-built API.

What three steps does a typical Server Action perform?

  • Render, hydrate, paint
  • Validate the input, mutate the data, then revalidate the affected UI
  • Fetch, cache, stream
  • Connect, query, disconnect

Answer: Validate the input, mutate the data, then revalidate the affected UI. Validate (never trust the client), mutate on the server, then revalidate (e.g. revalidatePath) so the affected route re-renders with fresh data.

Why is it safe to read process.env.DATABASE_URL inside a Server Component?

  • React encrypts environment variables
  • Because env vars are always public
  • Because Server Component code never reaches the browser, so secrets aren't exposed to visitors
  • It isn't safe under any circumstances

Answer: Because Server Component code never reaches the browser, so secrets aren't exposed to visitors. Server-only code is a real security boundary. Reading a secret in a Client Component would ship it to every visitor's dev tools.

Continue this course

Related lessons