Skip to main content
vinext implements React Server Components (RSC) for the App Router through integration with @vitejs/plugin-rsc. This enables the full App Router experience: server-first rendering, streaming, server actions, and automatic code-splitting.

How RSC Works in vinext

RSC requires coordination between three separate Vite environments:
1

RSC Environment

Runs with the react-server condition. Renders server components to an RSC stream (binary format).
2

SSR Environment

Runs on the server with the standard react-ssr condition. Consumes the RSC stream and renders HTML.
3

Client Environment

Runs in the browser. Hydrates interactive components from the RSC stream.

Multi-Environment Architecture

Critical: The RSC and SSR environments have separate module graphs. They do not share state. Per-request context (pathname, searchParams, params) must be explicitly passed via handleSsr(rscStream, navContext).

Server vs Client Components

Server Components (default)

By default, all components in the App Router are Server Components:
app/blog/page.tsx
Server Components can:
  • ✅ Use async/await for data fetching
  • ✅ Access backend resources (databases, file system, etc.)
  • ✅ Keep sensitive data server-side (API keys, credentials)
  • ✅ Reduce client bundle size (don’t ship to browser)
  • ❌ Use hooks like useState, useEffect
  • ❌ Use browser APIs
  • ❌ Attach event handlers

Client Components

Mark components with "use client" to make them interactive:
app/components/Counter.tsx
Client Components can:
  • ✅ Use React hooks (useState, useEffect, etc.)
  • ✅ Attach event handlers
  • ✅ Use browser APIs
  • ✅ Use navigation hooks (usePathname, useSearchParams, etc.)
  • ❌ Use async component functions
  • ❌ Access server-only APIs directly

Composing Server and Client Components

app/dashboard/page.tsx
app/dashboard/ClientChart.tsx
You can pass Server Components as children to Client Components:

The RSC Entry

vinext generates a virtual RSC entry module that handles routing and rendering:
packages/vinext/src/server/app-dev-server.ts
The generated entry exports a default handler:
virtual:vinext-rsc-entry (generated)

Rendering Flow

1. Route Matching

The RSC entry matches the URL to a route:

2. Layout Tree Construction

Layouts nest from root to leaf:
For a route like /blog/hello-world with:
The tree is:

3. RSC Stream Generation

React renders the tree to a binary RSC stream:
The manifest maps client component IDs to their JavaScript chunks.

4. SSR Consumption

The SSR entry receives the RSC stream and renders HTML:
virtual:vinext-app-ssr-entry (generated)

5. Client Hydration

The browser receives HTML with an embedded RSC stream and hydrates:
virtual:vinext-app-browser-entry (generated)

Server Actions

Server actions are functions marked with "use server" that run on the server:
app/actions.ts
Use in Client Components:
app/NewPostForm.tsx
When the form submits:
  1. Browser sends POST request to /_vinext/action/[hash]
  2. vinext deserializes the action reference and calls the server function
  3. Server action runs with FormData
  4. Response triggers re-render with updated data
Server actions support redirect(), cookies(), and headers() for full request control.

Streaming and Suspense

RSC enables progressive rendering with Suspense:
app/dashboard/page.tsx
vinext streams the HTML:
  1. Initial HTML with <h1> and skeleton
  2. When SlowComponent resolves, stream additional HTML
  3. Browser inserts content without full page reload
Loading files provide automatic Suspense boundaries:
app/dashboard/loading.tsx
Equivalent to wrapping the page in Suspense:

Metadata API

Generate <head> tags from Server Components:
app/blog/[slug]/page.tsx
vinext collects metadata during RSC rendering and injects it into the HTML <head>.

Configuration

Enable RSC by adding @vitejs/plugin-rsc to your Vite config:
vite.config.ts
For Cloudflare Workers deployment:
vite.config.ts

Limitations

vinext’s RSC implementation has some differences from Next.js:
  • Native Node modules (sharp, resvg) crash in dev mode but work in production builds
  • useSelectedLayoutSegment(s) derives segments from pathname rather than being truly layout-aware
  • Dynamic OG images using native modules work in production but not dev

Next Steps

Caching & ISR

Learn about the pluggable cache architecture

Server Actions

Deep dive into server actions

Metadata

Generate SEO-friendly meta tags

Deployment

Deploy RSC apps to Cloudflare Workers