TypeScript apps, run by a Rust server
FSR compiles routes, loaders, actions and pages into a plan at build time. The Rust server runs that plan for every request. Each feature below links to the tutorial that builds it.
How a request is served
Routes, loaders, actions and pages in TypeScript. Pages can be Tera templates instead.
Loaders become a plan. Islands become ES modules through snapfirec.
The host matches the route, runs the loaders and renders the page.
Islands hydrate when they become visible. Server islands send no component code.
What FSR does
Pages rendered in Rust
Loaders and pages compile to a plan that the Rust host runs. There is no JavaScript engine on the server, so the first request after a restart is as fast as later ones.
Tutorial(let count
(reduce (values (session cart)) 0
(fn (n q) (+ n q))))Server islands
The browser downloads no component code for a server island. A click sends the event to the server, which runs the handler and sends back the changed markup.
TutorialStreaming
The page is sent straight away and a slow section is filled in when its data arrives. You don't write a promise or a suspense boundary.
Tutorialimport type { Ctx } from "@snapfire/fsr";
export async function load({ params, services }: Ctx<"/product/{id}"> ) {
const product = await services.shopping.getProduct({ id: params.id });
const suggestions = await services.recommend.forProduct({ id: params.id });
return { product, suggestions };
}Session state
The cart is stored in the session on the server. Adding an item is one typed call and there is no client store to keep in sync.
Tutorialexport interface Session {
cart: Record<string, bigint>;
}
export const defaults: Session = {
cart: {},
};Typed service calls
The API client and its request and response types are generated from an OpenAPI document. A loader calls the service like a function.
Tutorialconst product = await services.shopping.getProduct({ id });
const reviews = await services.reviews.forProduct({ id });Third-party libraries
Add Chart.js without npm or node_modules. The library is copied into the app and loaded through the import map.
Tutorial$ fsr add shop/app chart.js@4.4.7 added chart.js chart.js/chart.bundle.mjs 202502 bytes
Deployment
fsr bundle writes a directory that you copy to the server with the binary. fsr doctor runs checks for failures that only show up in production.
Tutorial$ fsr doctor shop/app doctor 13 checks, nothing to report
Request traces
Each request is recorded as a tree of spans. It shows which loader waited on which service, what ran in parallel and which cache answered.
Tutorial$ fsr dev shop/app fsr server on http://127.0.0.1:3000/ fsr traces on http://127.0.0.1:3000/__fsr/traces
More features, each with a tutorial
No JavaScript, Just Fast IR
Real sources and the IR the compiler produced from them, regenerated on every build of this site. The last tab is a loader the compiler refuses and the error it reports.
import type { Ctx } from "@snapfire/fsr";
export async function load({ session }: Ctx) {
const count = Object.values(session.cart).reduce((n, q) => n + q, 0n);
return { count };
}(let count (reduce (values (session cart)) 0 (fn (n q) (+ n q))))
When to pick FSR and when not to
Where another framework fits your project better, this says which one.
Pick FSR when
- Your app is large. Each team deploys its own app. The host mounts them into one site with one session.
- Your backend is already Rust. Pages are served by the same binary as your API.
- You want no Node on the server. A deploy is one binary and a directory.
- Server render time matters. 74,606 requests a second, 1.4x React on V8 for the same page.
- Your team writes React or Vue. Both render on the server and share one store in the browser.
- Some pages need no TypeScript. Tera templates and web components sit beside React pages.
Pick something else when
- You need the largest ecosystem and hiring pool. Next.js has it.
- The site is mostly content. Astro ships no JavaScript by default and has CMS integrations.
- You deploy to serverless or the edge. SvelteKit, Nuxt and Astro have adapters for it. FSR runs as a server process.
- You want to write components in Rust. Leptos and Dioxus compile Rust to WebAssembly.
- You need a stable 1.0. FSR releases are 0.x and change between versions.