Type-safe APIs and React Server Components for Node.js, Bun, and Cloudflare Workers.
.use() for mounting sub-apps1npm install spiceflow@rsc
spiceflow@rsc, not spiceflow@latest.1npx -y skills add remorses/spiceflow
.md to any getspiceflow.com URL, for example https://getspiceflow.com/react-data.md..page() and .layout() for server-rendered UI with client interactivity:123456789101112131415161718192021222324252627import { 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)
.route() instead of .get()/.post() when you want to pass Zod schemas for validation — it accepts request, response, query, and params schemas.spiceflow and build type-safe APIs with Zod validation, streaming, OpenAPI, and a type-safe fetch client:123456import { Spiceflow } from 'spiceflow' const app = new Spiceflow() .get('/hello', () => ({ message: 'Hello!' })) app.listen(3000)
12345678// 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' })], })
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
"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.12345678910111213141516171819202122232425262728293031323334353637// 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 } }
| When you work on | Read |
Routes, Zod validation, typed errors, middleware, static files, CORS, streaming/SSE, .onError(), listen(), waitUntil, Node.js adapters, Next.js mounting, base path | API Framework |
| Calling the API with the typed fetch client, WebMCP browser tools | Fetch Client |
| Generating OpenAPI documents, response maps, hiding routes | OpenAPI |
| Exposing routes as LLM tools over Model Context Protocol | MCP |
| OpenTelemetry spans, Server-Timing, custom tracers | Tracing and Strada |
Sending non-JSON types (Date, Map, Set, BigInt) over the wire | Custom Serialization |
| When you work on | Read |
Vite setup, Tailwind, shadcn/ui, app entry, layouts, <Head> SEO, query params, client components, code splitting, router, Link, redirects, 404 pages | React Framework |
Loaders, useLoaderData, streaming with use(), forms, server actions, parseFormData, useActionState, ErrorBoundary | Data & Actions |
The SpiceflowRegister type registry, knownPaths, multi-app workspaces | Type-Safe Routing |
| Rendering remote components from another server | Federation |
| When you work on | Read |
| Writing vitest tests for routes, pages, and server actions | Testing |
| Auth middleware, proxying, non-blocking auth, cookie patterns, graceful shutdown | Middleware Patterns |
| Authenticating server actions and routes — they are public endpoints | Security |
| Porting an app from Remix or React Router | Migrate from Remix |
| How Spiceflow compares to Next.js, Hono, and Elysia | Comparisons |
| Installing and structuring shadcn/ui components | shadcn/ui |
A dependency crashing with useState is undefined at startup | use client trap |
| When you deploy to | Read |
Cloudflare Workers: setup, bindings, waitUntil, KV caching, edge cache | Cloudflare |
| Any host, to understand what happens across deploys | Deployment Skew |
| Cloudflare service bindings between Workers | Service Bindings |
| Docker or any container platform | Docker |
1234567891011121314import { 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')