Show, don't claim
This page ships 12 KB of JavaScript.
Everything interactive on this site, the wind animation, the copy buttons, and the scroll choreography, is 12 KB gzipped with Preact included. A page with no islands ships zero. For comparison, React and ReactDOM alone are about 45 KB gzipped, before any of your own code.
Measured on this site's own production build (it is built with Nowaki). Head-to-head, on the same one-counter app: a Nowaki page first-loads ~10 KB gzip, on par with Astro and roughly 5× less than Next's app-router baseline (~103 KB). Reproduce it with benchmarks/head-to-head.mjs.
Speed you feel on every keystroke.
Most JavaScript frameworks boot, transform, and rebuild on a JavaScript toolchain. Nowaki runs that whole pipeline in Rust (oxc), so the dev server is serving before a JavaScript bundler has finished warming up.
Dev server ready, measured on the example app.
To re-transform a changed file with oxc. No JavaScript bundler warm-up.
Production output: modules concatenated into one scope, tree-shaken, content-hashed, with source maps.
From nothing to a running app, without Rust.
npm create nowaki@latestScaffold
Lays down file-based routes/ and islands/. The CLI is a prebuilt binary, so there is no Rust to install.
npm run devDevelop
The Rust oxc pipeline transforms on demand. Islands hydrate; everything else stays HTML. Errors show as a full-screen overlay.
npm run build · startShip
Scope-hoisted, content-hashed ESM and SSR modules, served in production. Or prerender to static and put it on a CDN.
Write a route. Mark an island. Handle a form.
Routes are components with an optional server-only loader. A non-GET request runs the route's action. Components under islands/ are the only thing that hydrates.
A route with a loader
// routes/blog/[slug].tsx
import Comments from "../../islands/Comments.tsx";
// runs on the server only
export const loader = async ({ params }) => ({
post: await db.post(params.slug),
});
export default function Post({ data }) {
return (
<article>
<h1>{data.post.title}</h1>
<Comments postId={data.post.id} /> // only this hydrates
</article>
);
}A form with an action
// routes/guestbook.tsx
export const loader = (ctx) => ({ entries: read(ctx) });
// a non-GET request runs the action
export async function action(ctx) {
const form = await ctx.formData();
ctx.setCookie("guestbook", add(form.get("msg")));
return ctx.redirect("/guestbook"); // PRG
}File conventions
routes/_layout.tsxShared layout, nests per directoryroutes/_middleware.tsRuns before routes; auth, redirects, headersroutes/blog/[slug].tsxDynamic route + server loaderroutes/api/posts.tsGET / POST handlers, streaming Responseroutes/_404.tsx · _500.tsxNot-found and error pagesislands/Counter.tsxHydrates in the browser. Nothing else does.Honestly, next to Next and Astro.
None of these ideas is new on its own. Nowaki's bet is the combination. Here is where it actually differs, without spin.
| . | Nowaki | Next.js | Astro |
|---|---|---|---|
| Server-reactive islands (zero client JS)Only Nowaki | Jetstream — state on the server, HTML patches over a WebSocket, no component JS | RSC re-renders ship the React runtime | Server Islands defer SSR (one-shot, not live) |
| Toolchain | Rust (oxc), from scratch — and in the production request path | Turbopack (SWC), JS runtime | Vite → Rolldown, JS runtime |
| JavaScript by default | Zero, islands only | Ships React + hydrates | Zero, islands only |
| Full-stack app DX | Routing, loaders, actions, middleware, API, server functions | Yes, mature | Growing, content-first |
| Install without the toolchain's language | npm, no Rust | npm | npm |
| Maturity | Alpha | Mature, huge ecosystem | Mature |
Only here: a live, stateful island that ships zero component JavaScript. The server holds the state and pushes HTML patches over a WebSocket — LiveView's idea, fused with islands. RSC re-renders still ship React; Astro's Server Islands defer the initial render but don't update live. This is the one thing neither framework has.
Server functions, streaming SSR, plugins, and deploy adapters are parity, not differentiators — Astro and Next have their own. The real edge is Jetstream, the Rust production runtime, and hitting all of full-stack + zero-JS + npm-install at once. Next and Astro are mature and battle-tested; Nowaki is alpha.
Who it's for.
Nowaki fits apps that are content-heavy but still dynamic: marketing with auth, docs with interactive widgets, dashboards with real server data, commerce. The kind of page that is mostly text and server data, where a full-hydrate framework still ships a megabyte of JavaScript.
A good fit
- You write Next.js-style apps but most of each page is static content and server data.
- You want forms, auth, and dynamic routes without paying for a full client runtime on every page.
- You like the Remix-style loader and action model, with the parts shipped only where they're used.
Not the sweet spot yet
- Fully interactive single-page apps where almost everything is stateful. Islands fight you there, and full hydration or RSC fits better.
- Production-critical work today: Nowaki is alpha and the API still moves.
A real framework, not a static-site generator.
Built for dynamic apps in the Next.js and Remix lineage, with the parts you actually ship a product on.
Full-stack routing
File-based routes/ with nested _layouts, _middleware, server loaders, actions for forms, and api/ handlers with method dispatch and streaming.
Islands, zero JS by default
Pages render to HTML on the server. Only components under islands/ ship and hydrate, so a page costs only what it actually uses.
Server functions ("use server")
A module with a "use server" directive becomes an RPC boundary. Its exports run only on the server; the client gets a tiny fetch proxy (no implementation, no server-only deps). Dispatch is allowlisted, and getContext() exposes the request's cookies and headers.
Jetstream islands (server-reactive)
Mark an island export const live and it ships no component JavaScript: state stays on the server, clicks go over a WebSocket, the Rust server re-renders and pushes an HTML patch, and a ~2 KB runtime morphs it in. Presence, heartbeat, and connection scaling included. Client islands (optimistic UI) coexist on the same page.
Rust toolchain (oxc)
Parse, transform, resolve, bundle, minify, and scope-hoist run in Rust. Fast cold starts, millisecond rebuilds, and a persistent disk cache across restarts.
Installs with npm, no Rust
The CLI ships as prebuilt native binaries through npm's optional dependencies. No cargo, no toolchain, no postinstall.
Island-to-island SPA router
Navigation between island pages is client-side and instant, with prefetch and scroll restoration. Pages with no islands stay zero-JS and navigate normally.
CSS Modules, assets, source maps
*.module.css with scoped class names, hashed imports for images and fonts, and end-to-end source maps in dev and prod.
npm ecosystem, intact
SSR runs on a Rust-managed Node sidecar with Preact, so your existing packages keep working.
Honest dev experience
Full-screen error overlay, code-frame diagnostics, hot reload, and live island swap on save.
Honest about alpha.
Nowaki is young, but the core is real and verified end to end, including in headless Chrome. Here is exactly where it stands.
Works today
- dev / build / start / prerender
- Layouts, middleware, actions, API routes
- Islands + island-to-island SPA router
- Server functions ("use server") RPC
- Jetstream islands: server-reactive, zero client JS
- Jetstream presence + connection scaling (heartbeat, cap)
- Plugin virtual modules (resolveId / load) + transform hooks
- CSS Modules, asset imports, source maps
- Scope-hoisted production bundles
- Deploy adapters: Node, static, Bun, Deno, Cloudflare edge
- Streaming SSR, config plugins, TSRX (.tsrx) islands
- Head-to-head benchmarks vs Next and Astro
- Rust prod hot path + Rust-free install via npm
On the roadmap
- State-preserving (prefresh) HMR
- Scoped CSS for TSRX islands
- RSC-style streaming boundaries
- Stabilizing the public API toward 1.0