Skip to content
brust

brust starts from one observation: most pages on most sites are content. They do not need a client-side framework to display HTML, and they do not need to re-run the application on the client to become interactive in two or three places. brust is built so that the default cost of a page is the cost of serving HTML — and interactivity is something you add back deliberately, component by component.

Native-first rendering

A route marked native: true is compiled ahead of time: the JSX component becomes a template that the Rust side of the server renders directly with minijinja. At request time the route's loader runs in a Bun worker to produce the data, and the HTML is produced in Rust — no React executes on the server for that page, and no JavaScript is shipped to the browser by default.

{
  path: '/posts/{slug}',
  Component: Post,        // compiled to a template at build time
  native: true,
  loader: async ({ params }) => ({ post: await getPost(params.slug) }),
}

Because the template is data-driven, native routes trade some JSX expressive power for that speed: values you bind must be precomputed in the loader rather than computed inline. The native route pages cover the exact constraints.

Routes without native: true render with full React streaming SSR in a Bun worker — the escape hatch is always there, per route, when a page genuinely needs React on the server.

Islands for interactivity

Inside a native page, interactivity comes from islands: components you mount explicitly with <Island component={Counter} props={...} />. Only those components ship JavaScript, and each hydrates on its own schedule (load, idle, and so on). An ssr island is rendered to HTML in a worker first, so it appears instantly and can even be cached incrementally.

For lighter interactivity — a theme toggle, a dropdown — brust also has native behavior components: plain functions with x-* directives that run a small framework runtime instead of React. No virtual DOM, no hydration of markup that was already correct.

The result is a hydration budget you control line by line: the page is HTML, and the JavaScript bill is exactly the sum of the islands you placed.

When brust fits

brust is a good match when:

  • Content-heavy sites — documentation, blogs, marketing pages, listings. Most of the page is HTML; brust renders it in Rust and ships no JS for it.
  • Performance-sensitive server rendering — native routes and cache hits are served without waking a JavaScript worker at all.
  • Mixed pages — a product page that is 95% static with a cart button: native route + one island.

It is a weaker match when:

  • Your app is mostly interactive — if nearly every component needs client state (an editor, a dashboard of live widgets), the native-first advantage shrinks. You can build it with React streaming routes, but a client-first framework may serve you just as well.
  • You need a stable, battle-tested stack today — brust is alpha software (currently 0.1.x-alpha). APIs can change between releases.
  • You are not on Bun — brust is a Bun framework; there is no Node.js runtime support. Prebuilt native binaries cover macOS and Linux (glibc and musl).

Next

Install brust and scaffold a project: Installation.