# SnapFire FSR > SnapFire FSR serves TypeScript web apps written with React, Vue, Tera or web components from one Rust binary, with no JavaScript engine on the serving path. Independent teams deploy their own sites into one host that shares the layout, session and store. Each link below is the Markdown source of a page on this site. The same page without `.md` is its HTML. ## Guide - [What fsr is made of](https://snapfirers.com/fsr/the_guide/000-what-fsr-is-made-of.md): What is fsr, who is it for, what does it refuse to do and what do its terms mean in terms you already know? - [Calling a service without a client](https://snapfirers.com/fsr/the_guide/001-one-contract-no-client-code.md): How does a loader call a service it has never seen a client for, with the editor knowing the types and the runtime refusing a bad call? - [How loaders and actions are lowered](https://snapfirers.com/fsr/the_guide/002-a-body-is-data.md): Why is a loader read rather than run, what happens to one the build cannot read and why does the report always say where a body runs? - [Server rendering without a JavaScript engine](https://snapfirers.com/fsr/the_guide/003-rendered-where-it-is-cheapest.md): How does a React page arrive as HTML from a server that has no JavaScript engine and what does the browser do with it? - [Server components without React Server Components](https://snapfirers.com/fsr/the_guide/004-server-components-without-rsc.md): SnapFire FSR renders components on the server, streams a tree, mounts client modules into it and calls server functions by id. React Server Components do… - [Routes, loaders and pages](https://snapfirers.com/fsr/the_guide/100-routes-loaders-and-pages.md): How does a directory become a route, where does its data come from, what does its page receive and what happens when the user clicks a link? - [Actions and the session](https://snapfirers.com/fsr/the_guide/101-actions-and-the-session.md): How does the browser change something, how is the input typed, where does the session live and what stops an empty cart from reaching the order service? - [Components the server renders](https://snapfirers.com/fsr/the_guide/102-components-the-server-renders.md): What may a page or component say so that the server can render it, what happens to the parts only the browser can run and how do you know which is which? - [Testing a body and a page](https://snapfirers.com/fsr/the_guide/103-testing-a-body.md): How do you test a loader, an action or a page without a backend, without Node and without the test running somewhere the code does not? - [Islands in another framework](https://snapfirers.com/fsr/the_guide/104-islands-in-another-framework.md): How does a Vue component end up on a page the server rendered, what does the build do with a .vue file and what does an application that has no React in it… - [Applications without a framework](https://snapfirers.com/fsr/the_guide/105-no-framework-at-all.md): What does an fsr application look like with no component framework in it, where does the interactivity go and how does a region talk to the server without a… - [Two frameworks on one page](https://snapfirers.com/fsr/the_guide/106-two-frameworks-on-one-page.md): What does it actually take to run React and Vue in the same document, what do they share and what does it cost? - [Moving tests from Jest or Vitest](https://snapfirers.com/fsr/the_guide/107-moving-tests-from-jest-or-vitest.md): You have a suite written for Jest or Vitest with Testing Library. What changes when it runs under fsr test? - [Images and fonts](https://snapfirers.com/fsr/the_guide/108-images-and-fonts.md): For app developers. How an image a component imports becomes a served in the sizes a page needs, how a font under fonts/ reaches the page with no… - [Tailwind and themes](https://snapfirers.com/fsr/the_guide/109-tailwind-and-themes.md): For app developers. How a stylesheet that imports Tailwind gets its utilities from the classes your pages render, what the build says about a class it cannot… - [MDX and markdown](https://snapfirers.com/fsr/the_guide/110-mdx-and-markdown.md): For app developers. How a page or a piece of content is written in MDX or plain markdown, how the build lowers it like any other component, how a page picks a… - [Values the server and browser disagree on](https://snapfirers.com/fsr/the_guide/111-values-the-server-and-browser-disagree-on.md): What happens to a component that reads the local clock, the time zone, Math.random() or window (values the server and the browser would compute differently)… - [Moving an application from Next.js](https://snapfirers.com/fsr/the_guide/112-moving-an-application-from-nextjs.md): You have a Next.js application, App Router or Pages Router. What does fsr migrate rewrite, what does it leave for you and what do you run next? - [The stock host and its configuration](https://snapfirers.com/fsr/the_guide/200-the-stock-host-and-its-configuration.md): What runs an fsr application when nobody has written a server, where does it read its configuration and how do you know what it decided? - [Graduating to Rust](https://snapfirers.com/fsr/the_guide/201-graduating-to-rust.md): How does one loader, action or route move from TypeScript into Rust without the rest of the application noticing and what stops that from happening by accident? - [Services and transports](https://snapfirers.com/fsr/the_guide/202-services-and-transports.md): What happens between a body saying services.shopping.listProducts({}) and bytes leaving the process, who checks what and why application code can never see a… - [Sessions and identity](https://snapfirers.com/fsr/the_guide/203-sessions-and-identity.md): Where does the session a body reads actually live, who is the request and where does a login go? - [Reloading in place](https://snapfirers.com/fsr/the_guide/204-reloading-in-place.md): What changes when the application changes under a running host and what does not? - [Sites](https://snapfirers.com/fsr/the_guide/205-sites.md): How does a team own its part of the product, deploy it on its own schedule and still share the header, the sign-in and the navigation with everyone else? - [What a request did](https://snapfirers.com/fsr/the_guide/206-what-a-request-did.md): When a page is slow or wrong, how do you find out what actually happened, without wiring a collector first? - [The build and the dev loop](https://snapfirers.com/fsr/the_guide/300-the-build-and-the-dev-loop.md): What does fsr build produce, why is none of it committed and what does fsr dev do when a file changes? - [Dependencies without npm](https://snapfirers.com/fsr/the_guide/301-dependencies-without-npm.md): How does React get into the browser when there is no node_modules, where do the types for it come from and what changes when a team already has its own module… - [Imports and aliases](https://snapfirers.com/fsr/the_guide/302-imports-and-aliases.md): Why does a page write @src/ui/Header rather than ../../src/ui/Header, who understands that and what does the browser see? - [The deploy tree](https://snapfirers.com/fsr/the_guide/303-the-deploy-tree.md): What do you copy to a server and how does what it serves there stay the same as what it served under fsr dev? - [Checking a deployment](https://snapfirers.com/fsr/the_guide/304-checking-a-deployment.md): What is wrong with an application that starts, serves and answers, but does not do what its configuration says it does? - [Third-party scripts](https://snapfirers.com/fsr/the_guide/305-third-party-scripts.md): Where do an analytics tag, a consent banner, a font and a favicon go, given that an application has no document template, no head component and writes no… - [What a page asks other origins for](https://snapfirers.com/fsr/the_guide/306-what-a-page-asks-other-origins-for.md): When a visitor opens one of your pages, which requests leave for hosts other than yours? How do you keep one of them on your own origin when privacy or… - [Linting](https://snapfirers.com/fsr/the_guide/307-linting.md): How do you get what ESLint gives a TypeScript project, in an application that has no Node? - [Terminal apps with Ink](https://snapfirers.com/fsr/the_guide/400-terminal-apps-with-ink.md): For app developers. How an application written against Ink runs in the terminal with no Node, what the build does with each part of it, the hooks Ink's API is… - [The parts bin](https://snapfirers.com/fsr/the_guide/900-the-parts-bin.md): Every block in every crate, one line each, sorted by what you would be looking for. The chapters explain how the pieces fit; this is the index for when you… ## Tutorials - [Build your first page](https://snapfirers.com/fsr/learn/010-build-your-first-page.md): You'll scaffold an app, change its loader to return some data and put that data on a page. A Rust binary serves it. - [Build a counter](https://snapfirers.com/fsr/learn/020-build-a-counter.md): You'll add a working button. The browser downloads no component code for it. Clicking it sends the event to Rust, Rust runs the handler and sends back the… - [Add a working cart](https://snapfirers.com/fsr/learn/030-add-a-working-cart.md): You'll build a cart whose contents live in the session on the server. Adding an item is one typed call from the browser. The server holds the only copy of… - [Cache loaders and renders](https://snapfirers.com/fsr/learn/100-cache-loaders-and-renders.md): You'll make pages fast by letting FSR do the caching. FSR works out which routes read nothing of the request, renders those once and answers them from a… - [Prerender a blog at build time](https://snapfirers.com/fsr/learn/110-prerender-a-blog-at-build-time.md): You'll build a three-post blog where every post is a file on disk before the first request arrives. A post that isn't in the set still renders live, so a new… - [Find out what won't compile](https://snapfirers.com/fsr/learn/120-find-out-what-wont-compile.md): FSR turns your TypeScript into data a Rust host runs. Some code can't be expressed that way. You'll write some, read what the build says about it and see the… - [Validate a form](https://snapfirers.com/fsr/learn/130-validate-a-form.md): You'll build a sign-up form that rejects bad input, comes back with the message beside the field and leaves everything the visitor typed exactly where it was… - [Stream a slow page](https://snapfirers.com/fsr/learn/140-stream-a-slow-page.md): You'll take a page that waits four seconds on a recommendation service and make the rest of it arrive immediately, with the slow region filling in where it… - [Share state with the store](https://snapfirers.com/fsr/learn/150-share-state-with-the-store.md): You'll build an order page whose islands share state the way signals would: a badge in the header and a stepper on the page read one value, a picker writes… - [Add share cards](https://snapfirers.com/fsr/learn/160-add-share-cards.md): You'll give each page of a small recipe site its own share card: the picture a chat app or a social site shows when someone pastes the link. Each card is… - [Port async components from Next.js](https://snapfirers.com/fsr/learn/170-port-async-components-from-next-js.md): You'll write a report page the way Next's App Router teaches, with an async component that awaits its data. fsr will refuse it and tell you where the await… - [Write your islands in Vue](https://snapfirers.com/fsr/learn/200-write-your-islands-in-vue.md): You'll start a fresh app, point it at Vue and write a single-file component the server renders and the browser hydrates. Vue is the only framework in the… - [Write your islands in Svelte](https://snapfirers.com/fsr/learn/205-write-your-islands-in-svelte.md): You'll put a Svelte counter and a React counter on one page and make them share one number. Click either one and both move… - [Write islands as web components](https://snapfirers.com/fsr/learn/210-write-islands-as-web-components.md): You'll build a tool library. The pages are the same lowered templates as any other FSR app. The interactive bits are custom elements, which the browser… - [React and Vue on one page](https://snapfirers.com/fsr/learn/220-react-and-vue-on-one-page.md): You'll put a React island and a Vue island in the same document, have them share one piece of state, then navigate between a page whose island is Vue and a… - [Write your pages in Tera](https://snapfirers.com/fsr/learn/230-write-your-pages-in-tera.md): You'll build a noticeboard whose pages are Tera templates the host renders straight from the file. The routing, the loaders and the prerendering are the same… - [Style an app with Tailwind](https://snapfirers.com/fsr/learn/240-style-an-app-with-tailwind.md): You'll style a status page with Tailwind utilities. Tailwind CSS v4 is built into the bundler and the build hands it the exact list of classes your pages… - [Run one app on two domains](https://snapfirers.com/fsr/learn/300-run-one-app-on-two-domains.md): You'll make one binary answer for two host names and serve something different on each. The loader reads which domain it was asked on. A forged Host header… - [Put a docs site inside your app](https://snapfirers.com/fsr/learn/310-put-a-docs-site-inside-your-app.md): You'll mount a whole second FSR application inside the first one, at a prefix, sharing its navbar and its theme. The two applications can live in separate… - [Port a Next.js route](https://snapfirers.com/fsr/learn/320-port-a-next-js-route.md): You'll port a Next product page to FSR: the data fetch, the server action and the client component. You end up with a running app whose API client, request… - [Move a Next.js app with fsr migrate](https://snapfirers.com/fsr/learn/325-move-a-next-js-app-with-fsr-migrate.md): You'll run fsr migrate over a small Next.js app with a layout, metadata, a server action, next/link and next/image, read what it prints and finish the… - [Add a JavaScript library](https://snapfirers.com/fsr/learn/330-add-a-javascript-library.md): You'll add Chart.js with fsr add, draw a chart with it and see how FSR vendors the library and names it in the import map. Then you'll hit the… - [Add Google Analytics with a consent banner](https://snapfirers.com/fsr/learn/340-add-google-analytics-with-a-consent-banner.md): This continues the shop app from 330. You will add a cookie consent banner, load Google Analytics only after the analytics category is accepted and keep the… - [Protect a route](https://snapfirers.com/fsr/learn/350-protect-a-route.md): You'll put a page behind a sign-in, send an anonymous visit to the sign-in page and read who the visitor is from a loader. The check is one file and it runs… - [Sign in with a passkey](https://snapfirers.com/fsr/learn/355-sign-in-with-a-passkey.md): You'll let a signed-in visitor add a passkey, sign in with it on its own and then require it after the password. The browser's prompt runs from one client call… - [Sign in with Google](https://snapfirers.com/fsr/learn/356-sign-in-with-google.md): You'll switch the sign-in to Google, keep working against a stand-in form until the client secret exists and see the redirect the host sends once it does… - [Add light and dark themes](https://snapfirers.com/fsr/learn/360-add-light-and-dark-themes.md): You'll add light and dark that is correct in the first byte the server sends. A visitor who never chooses gets their system's setting. One who chooses keeps… - [Accept a file](https://snapfirers.com/fsr/learn/370-accept-a-file.md): You'll take a file from a form, check it before it goes anywhere and hand the bytes on. It works with JavaScript switched off. - [Call a GraphQL API](https://snapfirers.com/fsr/learn/375-call-a-graphql-api.md): You'll read countries from a public GraphQL API through a contract the build checks, handle the call failing and give a client an API key the application code… - [Embed a video](https://snapfirers.com/fsr/learn/380-embed-a-video.md): You'll put two YouTube videos on a page. The server renders each one as a still with a play button. The player only loads for the one a visitor actually… - [Answer an API route](https://snapfirers.com/fsr/learn/385-answer-an-api-route.md): You'll write a JSON API with a GET and a checked POST, answer a plain text download with your own Response and wrap every API call in middleware… - [Animate page changes with view transitions](https://snapfirers.com/fsr/learn/390-animate-page-changes-with-view-transitions.md): You'll build a grid of colors where clicking a tile grows it into the full swatch on the next page. Back shrinks it home again. It's the browser's own View… - [Keep page state across navigation](https://snapfirers.com/fsr/learn/395-keep-page-state-across-navigation.md): You'll build a page with a notes field, a packing checklist and a list of hikes. Open a hike, press Back and the page comes back the way you left it: the… - [Keep pages for reading offline](https://snapfirers.com/fsr/learn/397-keep-pages-for-reading-offline.md): You'll make an app installable, keep every page a visitor opened for when the network goes and tell an open page when a new build is ready. It takes one… - [Test a loader](https://snapfirers.com/fsr/learn/400-test-a-loader.md): You'll test a loader with fsr test. The test runs your loader through the same interpreter that serves requests, with its services stubbed, so nothing else… - [Ship it](https://snapfirers.com/fsr/learn/410-ship-it.md): You'll turn an app into a directory and a binary you copy to a server. First you'll run the checks that catch problems that only appear in production. - [Debug a page in production](https://snapfirers.com/fsr/learn/420-debug-a-page-in-production.md): You'll read a request back as one tree that shows which loader waited on which service, what ran at the same time and which cache answered. You'll do it first… - [Make a page fast](https://snapfirers.com/fsr/learn/430-make-a-page-fast.md): 420 reads a trace to find out what a request did. This tutorial uses the trace to find which part of a slow page to cut and to check that the cut worked. The… - [Find broken links](https://snapfirers.com/fsr/learn/440-find-broken-links.md): A page can render correctly while some of its requests fail. You'll learn what the deploy tree actually serves, why development never shows you the difference… - [Set a content security policy](https://snapfirers.com/fsr/learn/450-set-a-content-security-policy.md): You'll write a Content-Security-Policy, find out what it blocks before it blocks anything, read each violation in the dev panel and see why nonces don't work… - [Move a loader into Rust](https://snapfirers.com/fsr/learn/500-move-a-loader-into-rust.md): You'll take one loader out of TypeScript and answer it from Rust. The page, the route and the tests don't change. At the end you'll load the page and see your… - [Add FSR to an existing Rust service](https://snapfirers.com/fsr/learn/510-add-fsr-to-an-existing-rust-service.md): The FSR host is a tower::Service, so you can mount it inside a binary you already have, with your own runtime, middleware and listener. - [Serve Tera templates from Rust](https://snapfirers.com/fsr/learn/520-serve-tera-templates-from-rust.md): You'll build a notes board that renders from Tera templates, with its plan, loader and action written in Rust. At the end you'll add a note through a form… - [Run an Ink counter in the terminal](https://snapfirers.com/fsr/learn/600-run-an-ink-counter-in-the-terminal.md): You'll scaffold an app, write its root page as an Ink component and run it in the terminal with no Node. Then you'll edit it while it runs and keep the count. - [Lay out a task list](https://snapfirers.com/fsr/learn/610-lay-out-a-task-list.md): You'll turn the counter into a task list with a header, a selected row you move with the arrow keys and a footer of keys, all laid out with Ink's flexbox props. - [Read the command line](https://snapfirers.com/fsr/learn/620-read-the-command-line.md): You'll pass flags to the task list after `--`, read them in a loader and hand them to the page as props. The loader runs once, when the terminal opens. - [Save to a file](https://snapfirers.com/fsr/learn/630-save-to-a-file.md): You'll read the task list from a JSON file under your home directory and write it back on every change. A missing file starts the list with a default. - [Type into the app](https://snapfirers.com/fsr/learn/640-type-into-the-app.md): You'll add tasks from a text box and show a spinner while the list waits to be saved. Both come from modules FSR provides, so there is nothing to install. - [Move focus between panes](https://snapfirers.com/fsr/learn/650-move-focus-between-panes.md): You'll split the task list into a list pane and a detail pane, move between them with Tab and Shift+Tab and give each pane its own keys. - [Run git and show its output](https://snapfirers.com/fsr/learn/660-run-git-and-show-its-output.md): You'll run `git log --oneline` from an effect, collect what it prints and show it in a pane. A key runs it again. - [Keep time](https://snapfirers.com/fsr/learn/670-keep-time.md): You'll time a task with a clock a mount effect starts, quit with an exit code a script can test and lay the panes out again when the terminal is resized. - [Bring an Ink tool over](https://snapfirers.com/fsr/learn/680-bring-an-ink-tool-over.md): You'll move a small Ink notes browser into FSR with `fsr migrate`. It rewrites Node's modules, gray-matter and pino onto FSR's members and turns the props `render` passed into a loader. - [Open your app in a desktop window](https://snapfirers.com/fsr/learn/700-open-your-app-in-a-desktop-window.md): You'll open an ordinary FSR app in a native window, name the window and choose how the window reaches the host. The pages, loaders and islands are the… - [Open a file from the desktop app](https://snapfirers.com/fsr/learn/710-open-a-file-from-the-desktop-app.md): You'll add an Import CSV button to the shop from 700. In the desktop window it opens the system file dialog limited to CSV files and the shop imports the products… ## Docs - [Introduction](https://snapfirers.com/fsr/docs/000-introduction.md): These pages are the practical documentation for SnapFire FSR. They cover what it is, how to get an application running and how the pieces fit once you have… - [What SnapFire FSR is](https://snapfirers.com/fsr/docs/001-what-snapfire-fsr-is.md): FSR is a full-stack runtime for TypeScript applications that runs on Rust, with no JavaScript engine in the serving path. - [Getting started](https://snapfirers.com/fsr/docs/100-getting-started.md): You need Rust and nothing else. The build installs the two binaries it uses. - [The application model](https://snapfirers.com/fsr/docs/101-the-application-model.md): A directory under app/routes/ is a route when it holds a page.tsx, page.mdx, page.md or page.tera. The path is the directory path. - [Data and rendering](https://snapfirers.com/fsr/docs/102-data-and-rendering.md): Prerendered. A route whose loader reads nothing request-specific (no query, no session, no identity, no clock) can be rendered once at build time. The host… - [Islands and hydration](https://snapfirers.com/fsr/docs/200-islands-and-hydration.md): Most components never reach the browser. The build lowers them and the host renders them, so they only cost markup. A component qualifies when it is a… - [Actions and the session](https://snapfirers.com/fsr/docs/201-actions-and-the-session.md): A loader reads data and an action writes it. An action lives in actions.ts beside the page. It is declared with action instead of just exported, so the build… - [Sites](https://snapfirers.com/fsr/docs/202-sites.md): Add a [site] block to a configuration and the application becomes a site: every id the build emits is prefixed with its name and every route sits under its… - [What a request did](https://snapfirers.com/fsr/docs/203-what-a-request-did.md): A log line records one fact. Ten log lines from one request have to be reassembled by hand, interleaved with every other request in flight. A trace records… - [Vue, Web Components, htmx and Tera](https://snapfirers.com/fsr/docs/204-other-frameworks-and-none.md): A placement carries a module id and nothing else. The server writes around whatever it rendered there; the… - [The parts bin](https://snapfirers.com/fsr/docs/300-the-parts-bin.md): Make a directory under app/routes/ matching the path and put a page.tsx in it. routes/pricing/ serves /pricing; routes/product/[id]/ serves /product/{id} with… - [Diagrams](https://snapfirers.com/fsr/docs/400-diagrams.md): The host matches the route against the plan, runs the lowered loader, compiled or interpreted, walks the component tree and writes HTML. No JavaScript engine… ## Optional - [Compared](https://snapfirers.com/fsr/compare): FSR next to Next.js, Nuxt, SvelteKit, Astro, Remix 3, Leptos and Dioxus.