Agent-readable docs index: /llms.txt. Full docs in one file: /llms-full.txt. Download /docs.zip to grep all markdown files locally.

the simplest way to buildfull-stack React apps

Type-safe APIs and React Server Components for Node.js, Bun, and Cloudflare Workers.

Spiceflow is a type-safe API framework and full-stack React RSC framework focused on absolute simplicity. It works across all JavaScript runtimes: Node.js, Bun, and Cloudflare Workers. Read the source code on GitHub. Full documentation lives at getspiceflow.com.

Features

  • Full-stack React framework with React Server Components (RSC), server actions, layouts, and automatic client code splitting
  • Works everywhere: Node.js, Bun, and Cloudflare Workers with the same code
  • Type safe schema based validation via Zod
  • Type safe fetch client with full inference on path params, query, body, and response
  • Simple and intuitive API using web standard Request and Response
  • Can easily generate OpenAPI spec based on your routes
  • Support for Model Context Protocol to easily wire your app with LLMs
  • Supports async generators for streaming via server sent events
  • Modular design with .use() for mounting sub-apps
  • Built-in OpenTelemetry tracing with zero overhead when disabled

Installation

npm install spiceflow@rsc
Important
Spiceflow is still in pre-release. Install with spiceflow@rsc, not spiceflow@latest.

AI Agents

To let your AI coding agent know how to use spiceflow, run:
npx -y skills add remorses/spiceflow
Every documentation page is also available as raw markdown: append .md to any getspiceflow.com URL, for example https://getspiceflow.com/react-data.md.

Basic Usage

API routes return JSON automatically. React pages use .page() and .layout() for server-rendered UI with client interactivity:
import { Spiceflow } from 'spiceflow' import { Counter } from './counter' export const app = new Spiceflow() .get('/api/hello', () => { return { message: 'Hello, World!' } }) .layout('/*', async ({ children }) => { return ( <html> <body>{children}</body> </html> ) }) .page('/', async () => { return ( <div> <h1>Home</h1> <Counter /> </div> ) }) .page('/about', async () => { return <h1>About</h1> }) app.listen(3000)
When to use .route() vs .get()/.post()
Use .route() instead of .get()/.post() when you want to pass Zod schemas for validation — it accepts request, response, query, and params schemas.

Two Ways to Use Spiceflow

Spiceflow works as a standalone API framework or as a full-stack React framework — same router, same type safety, same code.
API only — no Vite, no React. Just install spiceflow and build type-safe APIs with Zod validation, streaming, OpenAPI, and a type-safe fetch client:
import { Spiceflow } from 'spiceflow' const app = new Spiceflow() .get('/hello', () => ({ message: 'Hello!' })) app.listen(3000)
Full-stack React (RSC) — add the Vite plugin to get server components, client components, layouts, server actions, and automatic code splitting. All API features still work alongside React pages:
// vite.config.ts import react from '@vitejs/plugin-react' import { defineConfig } from 'vite' import spiceflow from 'spiceflow/vite' export default defineConfig({ plugins: [react(), spiceflow({ entry: './src/main.tsx' })], })

How a Spiceflow App Works

Everything is one chained expression on a single Spiceflow instance: API routes, middleware, loaders, layouts, and pages. The chain is the source of truth for both runtime routing and TypeScript inference — the typed fetch client, router.href(), and useLoaderData() all read their types from typeof app.
new Spiceflow() ──> one chain, one type v ┌───────────────────────────────┐ .get / .post / .route (Zod) ├──> JSON + SSE APIs └───────────────────────────────┘ v ┌───────────────────────────────┐ .use(middleware) ├──> serveStatic, CORS, mounted sub-apps └───────────────────────────────┘ v ┌───────────────────────────────┐ .loader / .layout / .page ├──> React Server Components └───────────────────────────────┘ server actions, client chunks v app.listen(3000) / app.handle(request) ┌────────────┼────────────┐ v v v Node.js Bun Cloudflare Workers
For the React side, Vite builds three environments from the same entry: rsc (server components and actions), ssr (HTML rendering), and client (hydration and navigation). Every "use client" file automatically becomes its own browser chunk. Server actions marked "use server" become POST endpoints that re-render the page with fresh data after they run.
A realistic app entry looks like this:
// src/main.tsx import { Spiceflow, parseFormData } from 'spiceflow' import { Head, Link, router } from 'spiceflow/react' import { z } from 'zod' import { Counter } from './app/counter' export const app = new Spiceflow() .get('/api/hello', () => ({ message: 'Hello!' })) .loader('/dashboard/*', async ({ request }) => { const user = await getUser(request) return { user } }) .layout('/*', async ({ children }) => { return ( <html> <Head> <Head.Meta charSet="UTF-8" /> </Head> <body>{children}</body> </html> ) }) .page('/dashboard', async ({ loaderData }) => { return ( <div> <h1>Welcome {loaderData.user.name}</h1> <Counter /> <Link href={router.href('/')}>Home</Link> </div> ) }) .listen(3000) // Register the app type for type-safe routing everywhere declare module 'spiceflow/react' { interface SpiceflowRegister { app: typeof app } }

Documentation

The README is only an overview. The full documentation is split into feature docs — always read the doc for the feature you are working on before writing code. The API is small but opinionated, and the opinions are not guessable from other frameworks.

API framework

When you work onRead
Routes, Zod validation, typed errors, middleware, static files, CORS, streaming/SSE, .onError(), listen(), waitUntil, Node.js adapters, Next.js mounting, base pathAPI Framework
Calling the API with the typed fetch client, WebMCP browser toolsFetch Client
Generating OpenAPI documents, response maps, hiding routesOpenAPI
Exposing routes as LLM tools over Model Context ProtocolMCP
OpenTelemetry spans, Server-Timing, custom tracersTracing and Strada
Sending non-JSON types (Date, Map, Set, BigInt) over the wireCustom Serialization

React framework (RSC)

When you work onRead
Vite setup, Tailwind, shadcn/ui, app entry, layouts, <Head> SEO, query params, client components, code splitting, router, Link, redirects, 404 pagesReact Framework
Loaders, useLoaderData, streaming with use(), forms, server actions, parseFormData, useActionState, ErrorBoundaryData & Actions
The SpiceflowRegister type registry, knownPaths, multi-app workspacesType-Safe Routing
Rendering remote components from another serverFederation

Guides

When you work onRead
Writing vitest tests for routes, pages, and server actionsTesting
Auth middleware, proxying, non-blocking auth, cookie patterns, graceful shutdownMiddleware Patterns
Authenticating server actions and routes — they are public endpointsSecurity
Porting an app from Remix or React RouterMigrate from Remix
How Spiceflow compares to Next.js, Hono, and ElysiaComparisons
Installing and structuring shadcn/ui componentsshadcn/ui
A dependency crashing with useState is undefined at startupuse client trap

Deployment

When you deploy toRead
Cloudflare Workers: setup, bindings, waitUntil, KV caching, edge cacheCloudflare
Any host, to understand what happens across deploysDeployment Skew
Cloudflare service bindings between WorkersService Bindings
Docker or any container platformDocker

Testing

Test your spiceflow app directly with vitest. No browser, no build step, sub-second feedback. Call the app through the typed fetch client, call server actions as plain functions, and assert on responses:
import { createSpiceflowFetch } from 'spiceflow/client' import { SpiceflowTestResponse } from 'spiceflow/testing' import { app } from './main.js' const f = createSpiceflowFetch(app) // API routes return typed JSON const data = await f('/api/hello') expect(data).toEqual({ message: 'Hello, World!' }) // Page routes return SpiceflowTestResponse with rendered HTML const res = await f('/about') if (!(res instanceof SpiceflowTestResponse)) throw new Error('expected page') expect(await res.text()).toContain('About')
The spiceflow Vite plugin auto-detects vitest and configures everything. See the Testing guide for authentication patterns, stateful workflows, and dependency injection.

Comparisons

Spiceflow is a full React RSC framework, not only an API layer. Same app can serve JSON, pages, layouts, server actions, and OpenAPI.
Hono and Elysia are HTTP frameworks. Next.js is a React platform with file-based App Router caching. Spiceflow is both a typed API and a React app in one chain. Every request re-runs matching layouts. No static/dynamic split.
See Comparisons for Next.js, Hono, and Elysia in detail.