LangENKO

Rendering Modes

Mandu blends eight rendering strategies — here's when to use each.

since v0.22
On this page

Rendering Modes

Mandu is not one rendering strategy — it composes eight. Most pages pick a primary mode and mix in islands for interactivity.

The eight modes

Mode Runs Output Good for
Prerender Build time Static HTML Docs, marketing, blog
SSR Per request HTML Auth'd pages, user data
Streaming SSR Per request Chunked HTML Slow dependencies, Suspense
Island Client, after SSR/prerender JS bundle mounts on placeholder Interactive widgets inside static pages
Partial Client, after SSR/prerender Smaller than island Single buttons, form fields
"use client" page Client Full React app on page Dashboards, editors, games
SPA navigation Client, on link click Patches DOM, no reload Multi-page feel, single-page speed
Edge SSR Per request, at edge PoP HTML (low latency) Global users, per-region content

Decision tree

Does the page depend on the request (user, cookies, query)?
 ├── NO  → Prerender (fastest, zero server cost)
 │
 └── YES → How fast does the data resolve?
           ├── Fast (<200ms) → SSR
           ├── Slow / parallel → Streaming SSR with Suspense
           └── Fully interactive? → "use client" page

Need interactivity inside a static/SSR page?
 └── Use an Island (.island.tsx) or Partial
     ├── Many children interactive → Island
     └── One button, one form → Partial

Mixing modes

A single Mandu route can use three modes at once:

// app/dashboard/page.tsx (server component — SSR)
import ChartIsland from "./chart.island"; // <-- Island (client-hydrated)
import { DeletePartial } from "./delete.partial"; // <-- Partial

export default async function Dashboard() {
  const user = await loadUser(); // Server SSR
  return (
    <main>
      <h1>Welcome, {user.name}</h1>          {/* SSR */}
      <ChartIsland data={user.stats} />      {/* Island */}
      <DeletePartial.Render id={user.id} />  {/* Partial */}
    </main>
  );
}

The server renders the full tree once (HTML ships to browser), then the client hydrates only the island and partial — the rest stays static.

Default mode

If you write app/foo/page.tsx with no annotations:

  • No dynamic data → prerendered at build time
  • Has export async function generateStaticParams() → prerendered per param
  • Has server-only data fetching in the component → SSR per request
  • File starts with "use client"full client-rendered page (no prerender)

Mandu picks prerender by default when possible — it's the fastest path.

Edge vs origin

Set --target=workers | deno | vercel-edge | netlify-edge at build time (mandu build --target=workers). Edge SSR runs the same code as origin SSR but closer to the user. Not every Node API works at the edge — see Deploy to Cloudflare Workers for the compatibility list.

🤖 Agent Prompt

🤖 Agent Prompt — Rendering Modes
Apply the guidance from the Mandu docs page at https://mandujs.com/docs/architect/rendering-modes to my project.

Summary of the page:
Mandu supports 8 rendering modes. Pages default to prerender + island hydration. Use SSR for user-specific data, streaming SSR for slow pages with Suspense, 'use client' for fully interactive pages, partials for fine-grained hydration.

Then:
1. Make the change in my codebase consistent with the page.
2. Run `bun run guard` and `bun run check` to verify nothing
   in src/ or app/ breaks Mandu's invariants.
3. Show me the diff and any guard violations.

For Agents

AI hint

Mandu supports 8 rendering modes. Pages default to prerender + island hydration. Use SSR for user-specific data, streaming SSR for slow pages with Suspense, 'use client' for fully interactive pages, partials for fine-grained hydration.

Guard scope
architecture