A component language and compiler for resumable web apps.
brumate compiles single-file components into server-rendered HTML and small client chunks. The client does not hydrate. It reads the state that the server wrote into the page and loads the code for an event handler only when that event fires.
The compiler generates a server module that renders HTML with the state, and client modules that resume it. A Vite plugin compiles .brm files in an app.
This repository holds the compiler and its runtime, the type checker and editor support, the website, examples, benchmarks and smoke tests. brumate is at 0.x: the page format can change between versions, so the server and the client must come from the same version.
- Ship only the client code that an interaction can reach. Markup that never changes ships as HTML and nothing else.
- Never run component code again on the client to rebuild state.
- Keep the loader, the only code that loads before the page is used, under 1 KB. It is about 2 KB after gzip today.
- Keep the syntax small: scripts, markup with
#blocks, and a few compile-time functions.
<script>
import { $memo, $props, $signal } from 'brumate/signals';
import { prevent } from 'brumate/events';
import { persist } from './todos.server';
const { initial } = $props<{ initial: Todo[] }>();
let todos = $signal(initial);
let draft = $signal('');
const left = $memo(todos.filter((todo) => !todo.done).length);
async function add() {
todos = [...todos, { id: crypto.randomUUID(), title: draft, done: false }];
draft = '';
await persist(todos);
}
</script>
<h1>Todos</h1>
<form onSubmit=(prevent(add))>
<input value=draft onInput=((event) => (draft = event.currentTarget.value))>
</form>
<ul>
#for (todo of todos by todo.id) {
<li class={ done: todo.done }>{todo.title}</li>
}
</ul>
<p>{left} left</p>The <h1> produces no client code. The #for item template ships because add can change todos. persist is a server function, so the client only gets a call stub.
pnpm install
pnpm build
pnpm --filter example-todos devexamples/todos renders a todo list and a counter on the server. In the browser, only the loader runs until the page loads.
examples/tracker is a small issue tracker with sign-in, middleware, form validation, optimistic updates, streaming and a prerendered page. Run it with pnpm --filter example-tracker dev.
examples/hackernews is a Hacker News reader with feeds, threads of comments and users, cached on the server. Run it with pnpm --filter example-hackernews dev.
examples/ui-kit is a package of components, and examples/kit-app is an app that uses it.
The documentation is in website/docs, and the website shows it with samples that run in the page.
- Introduction and Getting started set up a project.
- Your first app builds a todo app step by step.
- The guides cover components, state, async data, server functions, routing, testing, publishing components and the rest.
- The reference lists every compile-time function, block, runtime export and plugin option.
- The internals describe the reactive graph, the page format and the server function protocol.
Design notes for contributors are in docs:
- Grammar defines the file format in EBNF.
- Intermediate representation defines the data that each compiler stage produces.
- Compiler describes the pipeline, the output and the runtime.
- Decisions lists what is decided and what is still open.
- Proposals lists features that would be good to add, with a sketch of each.
- Prior art compares other resumable and partially hydrated frameworks.
pnpm install
pnpm build
pnpm test
pnpm typecheck
pnpm lint
pnpm format
pnpm e2e # browser tests of the website and examples
pnpm bench # benchmarks, against other frameworks
pnpm --filter website check:samples # the live samples of the docs compileBuild first: the examples and the website use the built brumate package.
The tests in packages/brumate/test/fixtures parse and analyze each .brm file. The output of every stage is compared with a snapshot in test/__snapshots__. The tests in packages/brumate/test/render compile each component, render it on the server, and compare the modules, the HTML and the state with a snapshot. The tests in test/resume.test.ts load that HTML into a DOM, start the loader, and check that events update the page.
Minor work (features, minor fixes, docs of unreleased work) goes to pull requests against next. Hotfixes go to pull requests against main. Releases are published from main only, and next publishes nothing. After each release from main, merge main into next. A minor release promotes next into main in one pull request. See Decisions.
benchmarks builds the same three pages with brumate, Qwik 1, Qwik 2 and Marko 6: a small counter and todo app, a dashboard with 50 rarely used widgets, and a table of rows from js-framework-benchmark. It compares the client code each page loads, the size of the server-rendered HTML, how fast the server renders, and the work of the browser after a click. It needs a local Chrome for the browser metrics.
pnpm benchbenchmarks/README.md describes each metric and has the results of a run.
e2e builds the website and the examples, serves them, and uses them in Chrome. Run it with pnpm e2e.
Brumation is the dormant state of reptiles in cold weather. A reptile in brumation stays inactive but wakes up briefly to drink when it needs to, then goes back to rest. A brumate page does the same with its code. It loads a handler only when an event needs it.
MIT © lxsmnsyc
