Two months ago I shipped the Rasengan.js 2.0 beta and asked people to break it. They did, and today Rasengan.js 2.0 is stable.
Rasengan.js is a full-stack React framework built on Vite and React Router: file-based routing, SSR, static generation and SPA mode, with a backend story that now lives in the same project.
npx create-rasengan@latest
Here's what changed in 2.0.
A core built on web standards
The dev server and the production request handler used to run on Express. In 2.0 they run on Futon, a small HTTP runtime that works directly with the standard Request and Response objects, the same ones browsers, Bun, Deno and Cloudflare Workers already use. Server-side rendering moved from Node streams to Web Streams along with it.
You don't change anything in your pages or layouts. What you get is a core that isn't tied to Node anymore, which is what makes the next two sections possible.
API routes next to your pages
Backend endpoints can live inside your app, under src/app/_api/. Folders map to URL segments, the same way page routing works, and each HTTP method is a named export:
// src/app/_api/users/[id].route.ts
import { json, notFound } from 'rasengan/server';
import type { Context } from 'rasengan/server';
export async function GET(ctx: Context) {
const user = await findUser(ctx.params.id);
if (!user) {
return notFound('User not found');
}
return json(user);
}
That file is GET /api/users/:id. A middleware.ts in any folder applies to every route below it. No separate server, no extra deployment target.
Node.js, Bun or the edge
A new runtime option tells the build what it targets:
// rasengan.config.js
import { defineConfig } from 'rasengan';
import { rasengan } from 'rasengan/plugin';
export default defineConfig({
runtime: 'bun', // 'node' (default) | 'bun' | 'workerd'
vite: {
plugins: [rasengan()],
},
});
With 'bun', the production server runs on Bun with the same middleware stack it uses on Node. With 'workerd' and the Cloudflare adapter, the whole app is bundled into a single Worker. The Vercel and Netlify adapters were rebuilt on the new core too.
Under the hood, 2.0 also moves to Vite 8 and React Router 8.
What the beta caught
A public beta was the right call. A few of the bugs real projects found before stable:
- Client-side navigations in production got a full HTML document instead of the route's loader data.
- The built-in 404 page answered with HTTP 200.
- In dev, module-level state (a cache, a database client) was lost between requests.
- The worst one: in some apps, production SSR rendered an empty page shell. A second copy of React Router was getting bundled into the app's server code, with its own route context that never received any data. Nothing errored, the page was just empty until JavaScript loaded.
All fixed in 2.0.
Upgrading
From the beta, the one change to know about: react-router is now a peer dependency, so your app lists it directly:
npm install rasengan@latest react-router@^8.3.0
From v1, most apps only need a dependency bump and a rebuild. If you customized the server, rasengan/server no longer exports Express, and createRequestHandler now returns a standard (ctx) => Promise<Response> handler. The upgrade guide walks through it.
Try it
npx create-rasengan@latest
- Docs: https://www.rasengan.dev
- GitHub: https://github.com/rasengan-dev/rasenganjs
- Full announcement: https://www.rasengan.dev/blog/rasengan-2-stable
Thanks to everyone who tested the beta, filed issues and sent feedback. If you build something with it, I'd love to hear about it in the comments.
Top comments (0)