Claude Fable 5.1 & GPT-6 Astra packages are live

Server Components

Free

React Server Components — what runs where, the serialisation boundary, data fetching without waterfalls, and keeping secrets off the client.

202 lines7.8 KB Mistral Frontend
targetModels
Mistral Medium 3.5Mistral Large 3Mistral Small 4Mistral FamilyFuture Mistral Models
name
server-components
category
Frontend
description
React Server Components — what runs where, the serialisation boundary, data fetching without waterfalls, and keeping secrets off the client.
license
MIT
author
Agent.md maintainers
last-verified
reviewed-by
unreviewed
<!-- Generated from models/_canonical by scripts/build-model-variants.js. Edit the canonical source, not this file. Behavioural profile for Mistral: scripts/model-profiles.json -->

#How to apply this file

Each section opens with one imperative line; apply every rule in the section it introduces. Do not summarise or skip a section.


#Purpose

Rules for React Server Components. The model: components render on the server by default, ship no JavaScript, and can read data directly. Interactivity opts in with "use client".

The two things worth internalising: the boundary is a security boundary, and everything crossing it is serialised into the HTML.

Client-component specifics are Frontend/client-components; framework wiring is Backend/nextjs.


#What belongs where

[INST] Apply every rule in this section: What belongs where. [/INST]

Server componentClient component
Data fetching, database accessuseState, useReducer, useEffect
Secrets and API keysEvent handlers (onClick, onChange)
Large dependencies (markdown, syntax highlighting)Browser APIs (window, localStorage)
Static content and layoutAnimation, focus, scroll
Authorization decisionsAnything needing interactivity

Default to a server component. Push "use client" down the tree, to the smallest interactive leaf:

tsx
// Bad: the whole page becomes a client component, and every child ships to the browser
"use client";
export default function ProductPage() { … }

// Good: the page stays on the server; only the button is interactive
export default async function ProductPage({ id }) {
  const product = await db.product.findUnique({ where: { id } });   // server-only
  return (<article><h1>{product.name}</h1><AddToCart id={product.id} /></article>);
}

"use client" marks an entry point, not a single file: everything it imports also ends up in the bundle. One "use client" at the top of a layout ships the entire tree below it.


#The boundary is a security boundary

[INST] Apply every rule in this section: The boundary is a security boundary. [/INST]

ts
// lib/db.ts
import "server-only";        // importing this from a client component fails the build
export const db = new PrismaClient();
  • Mark every module holding secrets, database access or internal service calls with server-only. It converts a silent leak into a build error.
  • Props passed to a client component are serialised into the HTML and visible in view-source. Passing a whole user record sends the password hash to the browser.
tsx
// Leaks every column, including internal flags and hashes
<Profile user={user} />

// Explicit projection
<Profile user={{ id: user.id, name: user.name, avatarUrl: user.avatarUrl }} />
  • Only serialisable values cross: primitives, plain objects, arrays, Date, Map, Set, and Server Action references. Functions, class instances and Symbols do not — a Prisma model with methods will fail or silently lose them.
  • Authorization is enforced on the server, in the component or the data layer. A client component conditionally rendering nothing still received the data. → Backend/authorization

#Fetch without waterfalls

[INST] Apply every rule in this section: Fetch without waterfalls. [/INST]

Server components can await directly, which removes the client-side fetch-on-render waterfall. It also makes it easy to create a server-side waterfall by awaiting sequentially.

tsx
// Sequential — 300ms + 400ms
const user = await getUser(id);
const orders = await getOrders(id);

// Parallel — max(300ms, 400ms)
const [user, orders] = await Promise.all([getUser(id), getOrders(id)]);
  • Start independent requests together.
  • Fetch where the data is used, not high in the tree and drilled down. React deduplicates identical fetch calls within a render pass, so two components asking for the same thing cost one request.
  • Stream slow sections with <Suspense> so the fast part of the page appears immediately:
tsx
<Suspense fallback={<OrdersSkeleton />}>
  <Orders userId={id} />       {/* the rest of the page does not wait */}
</Suspense>
  • Provide a real skeleton with the same dimensions as the content, or streaming trades a slow page for a shifting one. → Frontend/performance

#Composition across the boundary

[INST] Apply every rule in this section: Composition across the boundary. [/INST]

A client component cannot import a server component — but it can render one passed as children:

tsx
// Server component page
<ClientTabs>
  <ServerRenderedPanel />     {/* stays on the server; only its output crosses */}
</ClientTabs>

This is the pattern that keeps a heavy dependency out of the bundle while still using an interactive shell around it. Use it before restructuring the tree.

Other rules:

  • A server component cannot use hooks, state, effects, or browser APIs. If you need one, you need a client component.
  • Context providers must be client components, but they can wrap server-rendered children.
  • Server components re-render on navigation and revalidation, not on interaction. Anything that must change on click belongs in a client component.

#Anti-patterns

[INST] Apply every rule in this section: Anti-patterns. [/INST]

Anti-patternWhy it failsFix
"use client" at the top of a page or layoutThe entire subtree ships to the browserPush it to interactive leaves
Secret module imported by a client componentThe secret is in the bundleserver-only
Passing a whole database row as a propSerialised into the HTMLExplicit projection
Passing a function or class instanceNot serialisable; fails or silently degradesPlain data, or a Server Action
Sequential independent awaitsServer-side waterfallPromise.all
Fetching high and prop-drillingCouples the tree; blocks streamingFetch where used
No <Suspense> around slow sectionsThe whole page waits for the slowest queryStream with boundaries
Skeletons of the wrong sizeStreaming causes layout shiftMatch the content dimensions
Hiding data with a client-side conditionThe data still reached the browserAuthorize on the server
Hooks in a server componentNot supportedClient component
Duplicating a fetch instead of relying on dedupExtra load for no reasonLet React deduplicate
Restructuring instead of children compositionHeavy dependencies pushed to the clientPass server output as children

#Checklist

  • Verify: Components are server components by default
  • Verify: "use client" appears at the smallest interactive leaves, not at layouts
  • Verify: Every server-only module is marked with server-only
  • Verify: No secret, database client or internal service is reachable from client code
  • Verify: Props crossing to client components are explicitly projected
  • Verify: Only serialisable values cross the boundary
  • Verify: Authorization is enforced on the server, not by conditional rendering
  • Verify: Independent data fetches run in parallel
  • Verify: Data is fetched where it is used, relying on request deduplication
  • Verify: Slow sections are wrapped in <Suspense> with correctly sized fallbacks
  • Verify: Server content is passed to interactive shells as children
  • Verify: No hooks, state or browser APIs appear in server components