Getting started
What you need
You need Rust and nothing else. The build installs the two binaries it uses.
cargo install snapfire_compiler
cargo install snapfire_fsr_clisnapfirec is the compiler that bundles TypeScript. fsr is the command you drive everything else with. Check them:
fsr --helpScaffold an application
fsr new storefrontThat writes a project directory: a config/app.toml, an app/ holding routes/, src/, styles/ and an importmap.json. There is no package.json and no node_modules. The project never gets either.
storefront/
config/app.toml
app/
importmap.json
routes/
layout.tsx
page.tsx
page.loader.ts
not-found.tsx
error.tsx
src/main.ts
styles/app.css
types/@snapfire/It does not add a framework. The import map names the client, its standard library and its store. The pages are written against @snapfire/fsr-authoring/template, which types the placements without React. A bare application is the default because most pages do not need one.
Ask for React with --with, which vendors it and adds the four import-map entries:
fsr new storefront --with reactfsr use <app> react does the same to a project that already exists. Both take vue, elements, htmx and tera as well. The scaffold prints the line for you:
next fsr use storefront/app react # only if the application wants React; also vue, elements, htmx or teraRun it
fsr dev storefront/appThe host boots, prints what needs attention and serves on the address in config/app.toml. fsr dev storefront/app --verbose prints the whole report, which is worth reading: it lists each route with its pattern, each loader with whether it lowered, each component with whether the server can render it and every static root. When something does not work, the report has usually already said so.
Edit app/routes/page.tsx and the browser reloads.
Commands
| Command | What it does |
|---|---|
fsr dev <app> | Build, serve and watch. The development loop. |
fsr build <app> | Build once, write generated/ and dist/. |
fsr check <app> | Typecheck without emitting. |
fsr serve <app> | Serve what is already built, no watcher. |
fsr test <app> | Run the application's tests, without a browser. |
fsr use <app> <direction> | Give the app react, vue, elements, htmx or tera. |
fsr add <app> <pkg@version> | Vendor a dependency and add its import-map entry. |
fsr types <app> | Fetch the type declarations the import map implies. |
fsr bundle <app> --out <dir> | Write the deploy tree. |
fsr prerender <app> | Render every eligible route to a file, paths included. |
fsr doctor <app> | Check a project or a deploy tree for what will bite in production. |
Where things live
config/app.toml holds the configuration: what to listen on, what the document's title is, how sessions are keyed, which service clients exist and where the static roots are. Most of it has a default. The report tells you what the host inferred.
It is the first of several files the host reads, in this order:
| File | When |
|---|---|
app.toml | always |
<release_env> | RELEASE_ENV |
<app_env> | APP_ENV |
<region> | APP_REGION |
<app_env>-<region> | both |
bundle.toml | in a deploy tree, written by fsr bundle |
Each is looked for as .toml, then .yaml. A missing file is normal: a checkout with only app.toml runs. A deployment adds its own overlay without touching the base. The boot report lists the files it actually read, in order.
app/importmap.json is the allowlist of bare specifiers. The compiler is given it as --import-map. An import with no entry fails the build rather than shipping a broken module. fsr add is what writes entries.
app/generated/ is written by every build and read by nothing you edit. It holds the plan the host serves, the route types your loaders constrain themselves against and the props types your pages import.
Try it
Change page.loader.ts to return a second field, then read it in page.tsx. Now delete the field from the loader but leave the page reading it: the build fails on that line before the browser ever sees the page. This is the contract described in What SnapFire FSR is.