Build with the platform,
not around it.
Juice is a React 19 Server Components framework with 2 dependencies. Your entire server is one function: (Request) => Response. No magic. No lock-in. No abstraction tax.
import { createRouter } from '@cmj/juice/runtime';
import manifest from './flight-manifest.json';
export default { fetch: createRouter(manifest) };That is the entire server. Four lines. Any runtime.
Not another Next.js
Juice does not abstract the web platform. It exposes it. Every route is a standard Request in, Response out. Every convention maps to a web standard you already know. When HTTP has a concept for something, Juice uses HTTP. When React has a primitive, Juice uses React. Juice only invents what neither provides.
Request in, Response out
No proprietary request objects. No custom response helpers. Standard web APIs from entry point to handler.
Manifest bridge
The compiler emits a JSON file. The runtime reads it. No magic module resolution. No framework-specific server hooks. Inspectable, diffable, versionable.
Zero Node.js APIs
The runtime uses Request, Response, ReadableStream, URLPattern. It runs on Cloudflare Workers without polyfills because it was built for Workers, not adapted to them.
What you get
File-Based Routing
Your filesystem is the router. Layouts, params, middleware — co-located with the routes they serve.
Async Data Fetching
Server components are async. Fetch data directly. Deduplicate with identity-based cache().
Server Actions
Forms that work without JavaScript. Enhance with useActionState. Real POST-Redirect-GET.
Streaming SSR
Three modes: buffered, shell, full stream. You pick the tradeoff between TTFB and correctness.
Typed Middleware
Onion-model middleware with createContextKey<T>(). Pass auth state without prop drilling.
Secure by Default
CSRF on, not opt-in. Prototype pollution prevented. Client boundaries enforced at build time.
CSS Pipeline
Global, modules, Sass. Vite builds it. The manifest tracks which CSS belongs to which route.
Any Runtime
One fetch handler. Cloudflare Workers, Bun, Deno, Node.js. No runtime-specific code.
Coming from Next.js?
If you have used Next.js and wished you could just return a Response, or used Remix and wished you had RSC streaming — Juice gives you both without the overhead.
| Concept | Next.js | Juice |
|---|---|---|
| Route handler | Proprietary NextRequest | Standard Request/Response |
| Middleware | Single file, edge only | Per-directory, onion model, everywhere |
| Data fetching | Magic fetch caching | Explicit cache(fn) dedup |
| Runtime | Node.js (edge opt-in) | WinterCG-first: Workers, Bun, Deno, Node |
| Server bundle | ~2MB+ runtime | 25KB gzipped |
| Streaming | Always on, limited control | Three modes — you choose |
SSR with full hydration
Juice is not just for content sites. Server components render on the server. Client components ('use client') hydrate on the browser with full React interactivity — useState, useEffect, onClick, forms, modals, everything.
Server renders first
HTML arrives fully rendered. No blank page, no loading spinner. Search engines see real content immediately.
Client hydrates
'use client' components become interactive after hydration. Only client components ship JavaScript to the browser — server components ship zero JS.
SPA navigation
After first load, page transitions fetch RSC payloads and swap content without full reloads. View Transitions API for smooth animations.
Be informed
Juice is early. Here is what you should know before choosing it.
No auth library, no CMS integrations, no community plugins yet. You will build integrations yourself or use platform APIs directly.
SPA navigations replace the full React tree. Client state in non-shared components is lost on navigation. Layout preservation is on the roadmap.
Juice gives you the platform, not abstractions over it. Your team should be comfortable with Request, Response, headers, and status codes.
Start building
npx @cmj/juice create my-app