Skip to content

Introduction

React Server Components as a Vite plugin, deployed wherever you like.

Updated View as Markdown

rsc-kit is a Vite plugin that gives you React Server Components. You write pages, layouts and server actions the way you would in the Next.js App Router; it renders them, streams the HTML, and turns links into navigations that replace only the part of the page that changed.

It brings no build system of its own — it is a Vite plugin — and it does not write a server for you either. Nitro builds one around your route tree, so where an app runs is a preset: Bun, Node, a Cloudflare Worker, Vercel, Netlify, Deno.

What an app looks like

src/app/
  layout.tsx         the document — <html>, <head>, <body>
  loading.tsx        Suspense fallback for everything below
  page.tsx           GET /
  posts/
    [slug]/
      page.tsx       GET /posts/:slug
  @modal/
    default.tsx      a parallel slot, empty until something fills it

Nothing registers those files. The plugin reads the directory at build time and generates the entry that knows about them.

src/app/posts/[slug]/page.tsxtsx
import { findPost } from '../../../data'

export default async function PostPage({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params
  const post = await findPost(slug)

  if (!post) return <h1>No such post</h1>

  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.body}</p>
    </article>
  )
}

A server component is an ordinary async function. It imports its data module directly, and neither the module nor its secrets reach the browser.

What you get

Streaming, not waterfalls

The shell paints before the data resolves. Suspense boundaries fill in as they finish, in whatever order they finish.

Navigations that keep the page

A navigation sends the layout chain it already has, and the server answers with only the part that changed. Scroll, focus and half-typed forms survive.

Parallel routes and interception

@slot directories render alongside the page. (.)folder opens a route as a modal over the page you were on, and as a real page on refresh.

Server actions

"use server" makes an async function callable from a client component. The body never ships; the call becomes one POST.

Queries, without an endpoint

query() marks a read. It travels as a GET, so it can be cached and prefetched — and reads that happen together leave as one request.

Frozen where it can be

Pages that ask for nothing dynamic are rendered at build time. Pages that do get their shell frozen and their data streamed.

An export target

The same app can build to a directory of files and be served by anything — including a CDN with no origin at all.

Where it runs

You do not write a server. Nitro builds one from your route tree, and where it runs is one line in your Vite config:

vite.config.tsts
nitro({ preset: 'bun' })

Swap the preset for node, cloudflare_module, vercel, netlify or deno and the same app deploys there instead. The build produces a .output directory; nothing else changes.

What it is not

Where to go next

Navigation

Type to search…

↑↓ navigate↵ selectEsc close