Skip to content
Back to skills

React Server Components

ASecurity

Covers React Server Components (RSC) in depth: the RSC rendering model, client/server boundary directives, serialization constraints, streaming with Suspense, Server Actions, composition patterns (slot, donut, data-down), 'use client' placement strategy, caching and revalidation, framework integration (Next.js App Router), and common RSC diagnostics. Use for \"Server Components\", \"RSC\", \"use client\", \"use server\", \"Server Actions\", \"server action\", \"RSC payload\", \"server compone...

  • 4 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 24, 2026
developmentjavascriptjavareactnextjsnoderefactoringapisecurityperformance

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 3 files and shows the line behind each finding

Scanned September 24, 2026

npx -y skills add chrishuffman5/domain-expert --skill react-server-components --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of React Server Components?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for React Server Components
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/chrishuffman5-react-server-components/badge)](https://www.skillsdirectory.com/skills/chrishuffman5-react-server-components)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: react-server-components
description: "Covers React Server Components (RSC) in depth: the RSC rendering model, client/server boundary directives, serialization constraints, streaming with Suspense, Server Actions, composition patterns (slot, donut, data-down), 'use client' placement strategy, caching and revalidation, framework integration (Next.js App Router), and common RSC diagnostics. Use for \"Server Components\", \"RSC\", \"use client\", \"use server\", \"Server Actions\", \"server action\", \"RSC payload\", \"server component\", \"client component boundary\", \"serialization\" questions. Do NOT use for general React questions (hooks, component model, Fiber) that don't involve the server/client boundary -- use the `react` skill. Do NOT use for Next.js App Router file conventions (page.tsx, layout.tsx, routing) -- use the `nextjs-app-router` skill."
license: MIT
---

# React Server Components

This skill covers React Server Components (RSC), stable in React 19, including:

- RSC rendering model: server-only rendering, RSC payload format, rendering pipeline
- Client/server boundary: `"use client"` and `"use server"` directives, module-level boundary
- Serialization constraints: what can and cannot cross the boundary
- Streaming and Suspense: progressive rendering, parallel data fetching, `use()` hook
- Server Actions: server-callable mutations, progressive enhancement, security model
- Composition patterns: data-down, slot (children), donut, shared components
- `"use client"` placement strategy: push to leaves, extract interactive parts
- Caching and revalidation: fetch caching, `revalidatePath`, `revalidateTag`, `React.cache()`
- Framework integration: Next.js App Router conventions, React Router v7, Waku
- Common diagnostics: hook-in-server-component errors, serialization errors, hydration mismatches

## How to Approach Tasks

1. **Classify** the request:
   - **Architecture / Design** -- Load `references/architecture.md`
   - **Patterns / Implementation** -- Load `references/patterns.md`
   - **Troubleshooting** -- Use the diagnostics section below
   - **Framework-Specific** -- Reference the framework integration section

2. **Identify the framework** -- RSC requires framework support. Next.js App Router is the primary production implementation. Clarify which framework is in use.

3. **Analyze** -- Apply RSC-specific reasoning. Consider the boundary between server and client, serialization constraints, and the streaming model.

4. **Recommend** -- Provide actionable guidance. Show where to place `"use client"` directives and how to structure the component tree.

## Core Concepts

### Rendering Model

**Server Components** render exclusively on the server. Zero JavaScript ships to the client. They can be `async` functions with direct `await` for data. They output an RSC payload (serialized React tree), not HTML.

**Client Components** (marked with `"use client"`) render on both server (SSR) and client (hydration + interactivity). Their JavaScript is included in the client bundle. They can use all hooks, event handlers, and browser APIs.

**Rendering pipeline:**
```
Server:
  1. React renders Server Components -> RSC payload
  2. RSC payload + Client Component SSR -> full HTML

Client:
  1. Browser displays HTML (fast first paint)
  2. React hydrates Client Components
  3. RSC payload reconciled with existing DOM
```

### Client/Server Boundary

The boundary is declared with file-level directives:

- `"use client"` at the top of a file makes that file and all components it exports Client Components. Everything imported by that file also becomes client-side. It is a module boundary, not a component boundary.
- No directive = Server Component by default (in RSC-aware frameworks).

**Boundary rules:**
```
Server Component -> can render Server Components (default)
Server Component -> can render Client Components (allowed)
Client Component -> CANNOT import Server Components (build error)
Client Component -> CAN receive Server Components as children/props (composition)
```

### Serialization Constraints

Props crossing the Server->Client boundary must be serializable.

**Can cross:** strings, numbers, booleans, `null`, `undefined`, `Date`, `TypedArray`, `Map`, `Set`, plain objects, arrays, React elements (JSX), `Promise` (enables streaming via `use()`).

**Cannot cross:** functions/closures (except Server Actions), class instances, Symbols, circular references, `RegExp`, DOM nodes.

This shapes data flow design. You cannot pass callback functions as props from Server to Client. Use Server Actions for mutations, or define handlers inside the Client Component.

### Server Actions

Functions marked with `"use server"` that execute on the server but are callable from Client Components.

```tsx
// actions.ts
'use server';

export async function deleteUser(formData: FormData) {
  const session = await requireAuth();
  const id = formData.get("id") as string;
  await db.users.delete({ where: { id } });
  revalidatePath("/users");
}
```

```tsx
// DeleteButton.tsx ("use client")
import { deleteUser } from './actions';

export function DeleteButton({ userId }: { userId: string }) {
  return (
    <form action={deleteUser}>
      <input type="hidden" name="id" value={userId} />
      <button type="submit">Delete</button>
    </form>
  );
}
```

**Security:** Server Actions are public HTTP endpoints. Always validate authentication, authorization, and inputs inside every action.

### Streaming and Suspense

RSC is designed for streaming. Async Server Components wrapped in Suspense boundaries resolve progressively.

```tsx
export default function Page() {
  return (
    <Suspense fallback={<ProductListSkeleton />}>
      <ProductList />  {/* async Server Component */}
    </Suspense>
  );
}

async function ProductList() {
  const products = await db.query('SELECT * FROM products');
  return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
}
```

Sibling async components fetch in parallel automatically. Waterfalls only occur when a child needs parent data.

Use `use()` to stream data from Server to Client Components:

```tsx
// Server Component passes Promise as prop
export default function Page() {
  const dataPromise = fetchData();
  return <ClientDisplay dataPromise={dataPromise} />;
}

// Client Component ("use client")
export default function ClientDisplay({ dataPromise }) {
  const data = use(dataPromise); // suspends until resolved
  return <div>{data.name}</div>;
}
```

## Composition Patterns

### Pattern 1: Data-Down (Most Common)

Server Component fetches data and passes it as props to a Client Component for interactivity.

```tsx
// Server Component
export default async function ProductPage({ id }) {
  const product = await fetchProduct(id);
  return <AddToCartButton product={product} />;
}

// Client Component ("use client")
export default function AddToCartButton({ product }) {
  const [added, setAdded] = useState(false);
  return <button onClick={() => setAdded(true)}>{product.name}</button>;
}
```

### Pattern 2: Slot (Server Component as Children)

Client Component wrapper receives Server Component content via children.

```tsx
// Modal.tsx ("use client")
export default function Modal({ children }) {
  const [open, setOpen] = useState(true);
  return open ? <dialog>{children}</dialog> : null;
}

// page.tsx (Server Component)
export default async function Page() {
  const data = await fetchModalData();
  return <Modal><ServerContent data={data} /></Modal>;
}
```

### Pattern 3: Donut (Server -> Client -> Server via Children)

The "donut" pattern enables Server Components deep inside Client subtrees.

```tsx
// layout.tsx (Server Component)
export default function RootLayout({ children }) {
  return (
    <html><body>
      <ClientThemeProvider>
        {children} {/* Server Components from the route */}
      </ClientThemeProvider>
    </body></html>
  );
}
```

### Pattern 4: Server for Expensive Computation

Offload heavy processing to the server with zero client bundle cost.

```tsx
import { marked } from 'marked'; // NOT shipped to client

export default function MarkdownContent({ raw }) {
  const html = marked.parse(raw);
  return <div dangerouslySetInnerHTML={{ __html: html }} />;
}
```

## "use client" Placement Strategy

**Core principle:** Push `"use client"` as far DOWN the tree as possible.

A component needs `"use client"` if it uses:
- `useState`, `useReducer`, `useContext`, `useEffect`, `useLayoutEffect`, `useRef` (DOM)
- Event handlers (`onClick`, `onChange`, etc.)
- Browser APIs (`window`, `document`, `localStorage`)
- Third-party libraries that use any of the above

**Refactoring pattern -- extract the interactive leaf:**

```tsx
// WRONG: entire card marked "use client" for one button
// ProductCard.tsx ("use client")

// CORRECT: only the button is a Client Component
// LikeButton.tsx ("use client")
export default function LikeButton() {
  const [liked, setLiked] = useState(false);
  return <button onClick={() => setLiked(!liked)}>Like</button>;
}

// ProductCard.tsx (Server Component)
import LikeButton from './LikeButton';
export default function ProductCard({ product }) {
  return (
    <div>
      <h2>{product.name}</h2>
      <p>{product.description}</p>
      <LikeButton />
    </div>
  );
}
```

**Common mistake locations:**
- `app/layout.tsx` -- cascades to every route
- `components/providers.tsx` -- use children slot pattern instead
- UI library wrappers that add a single `useState`

## Common Issues and Diagnostics

### "useState can only be used in a Client Component"

Hook used in a Server Component. Add `"use client"` to the file, or extract the stateful part into a separate Client Component (preferred).

### "Functions cannot be passed to Client Components"

Function prop crossing the boundary. Use a Server Action (`"use server"`), move logic to the Client Component, or pass only primitive data.

### Hydration Mismatch

Server/client render difference. Use `useEffect` for client-only content, `suppressHydrationWarning` for intentional mismatches, or `data-allow-mismatch` (React 19).

### "use client" Too High

Performance degrades, bundle size grows. Audit `layout.tsx` and `providers.tsx`. Extract providers using the children slot pattern.

### Server Action Security

Actions are public endpoints. Always check auth, validate inputs with Zod, return minimal data.

## Framework Integration

### Next.js App Router (Primary)

Directory conventions: `page.tsx` (route), `layout.tsx` (wrapper), `loading.tsx` (Suspense fallback), `error.tsx` (error boundary, must be `"use client"`), `route.ts` (API handler).

Caching layers: Request Memoization, Data Cache, Full Route Cache, Router Cache. Invalidate with `revalidatePath`, `revalidateTag`.

### React Router v7 / Remix

RSC-like capabilities via loaders and actions. Full RSC support is in active development.

### Waku

Lightweight RSC framework closer to React primitives. Suitable for learning RSC concepts.

## Reference Files

- `references/architecture.md` -- RSC rendering pipeline, boundary rules, serialization, framework integration
- `references/patterns.md` -- Data fetching in RSC, composition patterns, "use client" placement, caching

## Quick Reference

### Directive Summary

| Directive | Where | Effect |
|---|---|---|
| *(none)* | Any file | Server Component (default in RSC frameworks) |
| `"use client"` | Top of file | Module becomes Client Component boundary |
| `"use server"` | Top of file or function | Marks Server Actions |

### Component Capability Matrix

| Capability | Server | Client |
|---|---|---|
| `async`/`await` in render | Yes | No (use `use()`) |
| Hooks (`useState`, `useEffect`) | No | Yes |
| Browser APIs | No | Yes |
| Direct DB/filesystem access | Yes | No |
| JS shipped to browser | No | Yes |
| Renders in SSR | Yes | Yes |

### Prop Serialization Quick Check

```
string, number, boolean, null, undefined -> OK
Date, TypedArray, Map, Set -> OK
Plain objects, arrays, React elements, Promises -> OK
Functions -> NOT OK (use Server Actions)
Class instances, Symbols, closures -> NOT OK
```

Files in this skill

  • SKILL.md11.9 KB
  • references/architecture.md8 KB
  • references/patterns.md7.6 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…