Compared

FSR and seven other frameworks

Teams build and deploy their own sites on their own schedules. One Rust binary serves them all under one layout, session and store, with no Node. Below, FSR next to five JavaScript frameworks and two Rust ones, on the questions that decide a project.

  • One runtime for independently developed sites.
  • One shared application model across UI frameworks.
  • Native rendering without a JavaScript engine on the serving path.
TypeScript, served by RustJavaScriptRust
SnapFire FSRExcerion SunNext.jsVercelNuxtNuxtLabsSvelteKitSvelte teamAstroAstroRemix 3ShopifyLeptosCommunityDioxusDioxusLabs
Best for
Best forLarge appsIndependent teams each deploy their own site into one app, served by one Rust binary.React SaaSSEO-heavy React apps and e-commerce.Vue appsVue content sites and SaaS.Interactive UIsApps where bundle size matters.Content sitesBlogs, docs and marketing.Full-stack server appsA fetch-based server that renders HTML and hydrates only the components you mark.DocsAll-Rust teamsApps written end to end in Rust.Cross-platform RustOne Rust codebase for web and native.
Subsites
SubsitesNative microfrontendsEach team builds and deploys its own app. The host mounts it at a path inside the shared layout, session and store.GuideMulti-zonesSeparate apps joined by rewrites. Crossing zones reloads the page.DocsLayersLayers merge apps at build time. Separately deployed apps can share a domain under different baseURL paths.DocsBase path onlyEach app mounts under its own paths.base behind a proxy. Crossing apps is a full page load with no shared layout or state.DocsBase path onlyEach app mounts under its own base behind a proxy. Links between them are full page loads.DocsNot built inrouter.mount() splits one deployment. A separately deployed part sits behind fetch-proxy.DocsBase path onlyRouter base mounts one app under a path. Separate apps go behind a proxy.DocsBase path onlyEach app sets base_path to mount under its own path behind a proxy.Docs
Cross-island state
Cross-island stateOne store across islandsLoaders seed a keyed store the server renders from. React and Vue islands read the same keys, with optimistic writes and rollback.GuideShared React treeContext and state libraries span the page. Only Client Components read them.DocsuseState built inShared state carried from the server to the browser. Server components cannot read it.DocsOne appRunes and context span the page.DocsLibraryNano Stores between islands. Nothing seeded by the server.DocsA model through contextAn event target made at the nearest shared client boundary and passed down.DocsSignals and contextShared in a hydrated app. In islands mode context passes only to nested islands.DocsOne appSignals and context span the page.Docs
Frameworks
FrameworksReact, Vue, Tera and web componentsAll four render on the server.ReactServer and client components.VueVue single-file components.SvelteSvelte components.React, Vue, Svelte, Solid, PreactMixed on one page as islands.Its own JSX componentsremix/component, a JSX runtime of its own. React libraries do not work.DocsLeptos componentsWritten in Rust with the view! macro.Dioxus componentsWritten in Rust with RSX.
SSR or CSR
SSR or CSRAutomaticServer-rendered by default. The build moves only what needs the browser into islands.Per componentYou mark browser code with "use client".Per routeRoute rules pick SSR, static or SPA.Per routePage options turn SSR, CSR and prerendering on or off.Per islandStatic by default. You add a client directive to hydrate.Per componentPages render on the server. Components wrapped in clientEntry() hydrate.DocsYou chooseSSR with hydration, CSR or islands, set by feature flags.You chooseFullstack SSR with hydration. Also builds desktop and mobile apps.
Rendering modes
Rendering modesSSR, SSG, ISR, streaming, SWRPrerendered pages regenerate while the host serves. Service data serves stale while it refreshes. Server islands and SPA navigation.GuideSSR, SSG, ISR, streamingPartial prerendering mixes static and dynamic.SSR, SSG, SPA, SWRRoute rules set each one per route.SSR, prerender, CSRLoad functions can stream promises.Static, SSR, server islandsServer islands render after the page.Streaming SSRFrames with a fallback stream in later. No prerender or static export.DocsStreaming, islands, CSRIn-order and out-of-order streaming.SSR, streaming, SSGPlus CSR, desktop and mobile.
Performance
Performance74,606 req/s1.4x React on V8 for the same page. 12.5 µs a render.ExcellentPartial prerendering and edge rendering.StrongNitro server with hybrid rendering.EliteCompiles components away. Tiny bundles.EliteZero JavaScript by default.Unbundled modulesES modules through an import map. Only client entries ship JavaScript.EliteFine-grained reactivity. Ships WebAssembly.HighShips WebAssembly to the browser.
Loading and isolation
Loading and isolationIslands load on their ownEach island fetches its own code and mounts in its own root, on load, on view or when idle. One that fails leaves the rest working.GuideOne React treeSuspense boundaries split hydration. error.tsx contains a failure to its segment.DocsOne appLazy hydration per component is opt-in. Server components are experimental.DocsOne appThe page hydrates as one component tree. Streamed promises and svelte:boundary keep a slow or failing part from blocking the rest.DocsIslands per componentEach island hydrates on its own directive. A slow island does not block the others.DocsFrames load on their ownA frame with a fallback is its own request. If it fails the fallback stays.DocsOpt-in islandsWhole-app hydration unless islands mode is on. Then islands hydrate one after another in page order, from one WASM binary.DocsOne appHydrates as one WebAssembly app. The server waits for every suspense boundary unless out-of-order streaming is on.Docs
Stores and signals
Stores and signalsShared store, local stateuseStore over typed keys, with derive, transaction and optimistic. React's useState and Vue's ref and computed hold what one island owns.React stateuseState and context. Signals only through a library.Vue refsref, computed and reactive are signals.Runes$state and $derived are signals compiled into the component.Per frameworkEach island keeps its own framework's state. Nano Stores to share.Variables and update()State is plain variables. handle.update() re-renders. No signals or store.DocsFine-grained signalssignal() and memos update only the DOM that reads them.Signalsuse_signal and memos re-render the components that read them.
Streaming and boundaries
Streaming and boundariesStreams per segmentloading.tsx and error.tsx beside any route. A failing layout or slot loader degrades only its segment.GuideSuspense per segmentloading.js and error.js per route segment. Parent layouts stay up.DocsExperimental streamingA fatal server error replaces the page with error.vue. NuxtErrorBoundary catches client errors only.DocsStreamed promises+error.svelte catches load errors at the nearest route. svelte:boundary catches render errors in place. No loading file.DocsIn-order streamingServer islands load on their own. A render error on an on-demand page shows 500.astro.DocsFrame fallbacksNo error boundary component. resolveFrame decides how a failed frame shows.DocsOut-of-order by defaultSuspense and ErrorBoundary are components, not route files.DocsOpt-in out-of-orderSuspense and error boundaries are components, not route files.Docs
Parallel and intercepting routes
Parallel and intercepting routesSlots and interceptsA slot is a parallel segment with its own loader. page.<slot>.tsx opens a route over the page, applied on the server.GuideSlots and interception@slot folders and (.) intercepts that show a route as a modal.DocsNamed viewsNo interception or modal routes.DocsShallow routingModals tied to history in the browser. The server does not see them.DocsNot built inOne page at a time.DocsNamed framesA side panel can be a named frame backed by its own route. No intercepting routes.DocsNested outletsNo parallel or intercepting routes.DocsLayouts and OutletNo parallel or intercepting routes.Docs
Caching and revalidation
Caching and revalidationRender memo and tagged dataThe host memoizes renders by their data. Service calls cache for a TTL, serve stale while they refresh and drop the tags a write names.DocsTagged cacheuse cache, cacheTag and revalidateTag. ISR for unknown params.DocsRoute rulesswr and isr per route. Keyed data and cached functions without tags.DocsLoad invalidationinvalidate re-runs loads. ISR only through the Vercel adapter.DocsRoute cache with tagsEntries purge by tag or path. Needs a cache provider, with an in-memory one built in.DocsHeaders onlyCache-Control, ETags and Vary per response. No revalidation API.DocsStatic routesPrerendered or incremental pages. No tagged data cache.DocsIncremental rendererPages cached in memory and on disk with an optional expiry. Invalidation is per route, with no tags.Docs
Built in
Built inImages, fonts, sessions and authAlso uploads, validation, i18n, CSP, card images and a sitemap.Images and fontsAuth and sessions from libraries.Official modulesImage, fonts and auth utilities as modules.CookiesImages through a plugin. Auth from libraries.Images, sessionsFonts API is experimental. Auth from libraries.Sessions, auth, dataSession stores, OAuth providers, CSRF middleware, a SQL toolkit and image transforms.DocsServer functionsImages, sessions and auth from crates.Asset bundlingImages optimised at build. Auth and sessions from crates.
Share cards
Share cardsDrawn in Rustopengraph-image.tsx beside a page draws from the page's data. No browser or Node renders it.GuideImageResponseopengraph-image.tsx returns an ImageResponse, drawn with Satori and Resvg on the server. Cached at build unless it reads the request.DocsCommunity modulenuxt-og-image draws a Vue component with Takumi or Satori, at build for prerendered pages.DocsNot built inA +server.js endpoint draws it, usually with satori and resvg.DocsNot built inCommunity astro-og-canvas or Satori in a .png.ts endpoint.DocsNot built inA route returns any Response, so an image route with satori and sharp.DocsNot built inleptos_meta writes og:image tags. Drawing the image is up to you.DocsNot built indocument::Meta writes og:image. Drawing the image is up to you.Docs
Tailwind
TailwindCompiled in RustTailwind v4 ported to Rust, fed the classes the build found. No Node and no content globs.GuidePostCSS plugin from npmtailwindcss and @tailwindcss/postcss from npm. create-next-app sets it up by default.DocsVite plugin from npmtailwindcss and @tailwindcss/vite from npm, added to vite.plugins.Docssv add tailwindcssInstalls Tailwind's Vite plugin from npm and edits the stylesheet.Docsastro add tailwindInstalls @tailwindcss/vite from npm and writes the stylesheet.DocsNot built inRun Tailwind's CLI and serve its output. The asset server has no Tailwind step.DocsStandalone CLIcargo-leptos downloads Tailwind's standalone binary when tailwind-input-file is set. No npm.DocsAutomatic, no npmdx starts Tailwind's standalone binary when tailwind.css sits beside Cargo.toml.Docs
Light and dark
Light and darkChosen on the servertheme.ts picks the scheme per request and the host writes it on <html>. No script runs before paint.DocsInline scriptNo theme API. The docs have an inline script in the head set data-theme before paint.DocsModule's inline script@nuxtjs/color-mode puts a script in the head that sets the class before paint.DocsNot built inmode-watcher sets the scheme from a head script. A handle hook can write it from a cookie.DocsYour own inline scriptThe tutorial reads localStorage in an is:inline script and sets a class on <html>.DocsNot built inA demo reads a cookie in middleware and renders data-theme on <html>.Docsleptos-useuse_color_mode reads a client hint or a cookie on the server. Without one the first visit can flash.DocsNot built inAn inline script in a custom index.html sets it before the WebAssembly loads.Docs
Sitemap and robots
Sitemap and robotsBuilt in[crawl] serves robots.txt and a sitemap of every page an anonymous visitor is served, from the routes and paths exports.DocsFile conventionsapp/sitemap.ts and app/robots.ts return typed data. Static files work too.DocsTwo community modules@nuxtjs/sitemap and @nuxtjs/robots from Nuxt SEO.DocsNot built inThe docs show a sitemap as a +server.js endpoint. robots.txt goes in static/.DocsOfficial sitemap integration@astrojs/sitemap writes the sitemap. robots.txt is yours to write.DocsNot built inRoutes that return text or XML, built with the route map's href helpers.DocsNot built inAn Axum handler over generate_route_list builds the sitemap.DocsNot built inNo sitemap is written. A robots.txt in public/ is copied as it is.Docs
MDX
MDXBuilt in, a Cargo featurepage.mdx and imports lower like any component, with frontmatter, collections and code highlighted at build.Docs@next/mdxMDX pages and imports with mdx-components.tsx. Frontmatter needs a remark plugin.DocsMDC, not MDXNuxt Content places Vue components in Markdown with its own MDC syntax.Docsmdsvex, not MDXsv add mdsvex mixes Svelte components into Markdown.DocsOfficial integration@astrojs/mdx makes .mdx pages and content collection entries.DocsNot built inNo MDX compiler. Render Markdown with a library of your own.DocsNot built inClosed as not planned. Community crates render Markdown to views.DocsNot built inCommunity crates such as dioxus-mdx. None is official.Docs
View transitions
View transitionsOne optionenableNavigation({ transitions: true }) swaps pages inside a view transition.GuideReact ViewTransitionRoutes animate inside <ViewTransition> from React 19.3, with no configuration.DocsExperimental flagexperimental.viewTransition turns on the View Transitions API.DocsonNavigate, by handCall document.startViewTransition inside onNavigate in the root layout.DocsClientRouter<ClientRouter /> animates page changes, with transition:name and transition:persist.DocsNot by defaultSoft navigations skip view transitions. Wrap navigate() in startViewTransition yourself.DocsOpt-in prop<Routes transition=true> uses the View Transition API.DocsNot built inThe router has no view transitions. The community dioxus-motion crate adds animated outlets.Docs
Keeping a page
Keeping a pageOne optionenableNavigation({ activity: true }) keeps the last ten pages' DOM. React 19.2 islands keep their state.GuideWith cacheComponentsThe last three routes are hidden under React's <Activity> instead of unmounted.DocsKeepAlive, opt-inkeepalive on <NuxtPage> or in definePageMeta keeps a page's state.DocsSnapshots, by handDOM state is discarded unless a page exports a snapshot that captures and restores it.DocsPer elementtransition:persist carries an element or island to the next page when it appears there too.DocsRefetched on backBack and forward reload the page from the server. data-rmx-preserve-dom keeps marked elements.DocsNot built inThe router replaces the old route's view and has no keep-alive or snapshot API.DocsNot built inThe Outlet renders only the active route, so leaving a page drops its state.Docs
i18n
i18nBuilt in, routing to formattingLocale from prefix, cookie or header. Message catalogs and number and date formatting that match on server and browser.Do it yourselfProxy plus a [lang] segment and your own dictionaries.Community moduleRouting and messages through @nuxtjs/i18n.CLI add-onsv add paraglide sets up routing and messages.Routing built inPrefixes, fallbacks and domains. Translations from libraries.Not built inThe official demo uses i18next in middleware with Intl for formatting.DocsCommunity crateleptos_i18n for messages and formatting. leptos_i18n_router adds locale routing.Community cratedioxus-i18n for Fluent messages. No locale routing.
Security
SecurityCSP built inThe host hashes every script and style it emits into the policy. New apps start report-only and fsr dev names the fix for each violation.GuideCSP by handNonces set in proxy, which forces dynamic rendering. Experimental SRI hashes keep pages static.DocsThrough a moduleThe third-party nuxt-security module adds headers and a CSP with nonces or hashes.DocsCSP built inThe csp config adds hashes or nonces for its own inline scripts and styles.DocsCSP hashesThe security.csp config hashes its scripts and styles. It does not work with ClientRouter.DocsNo nonce yetInline import map and styles carry no nonce. Nonce support is an open pull request.DocsNonce featureWith the nonce feature on, its inline scripts carry a nonce. The policy is yours to set.DocsNo nonce supportServer rendering writes inline hydration scripts with no nonce. The header is yours to set.Docs
Deployment
DeploymentOne binaryCopy it and a directory to any server. No Node.Vercel firstRuns on any Node server.Many runtimesNode, workers and static hosts.AdaptersNode, static, serverless and edge.Static or SSRAdapters for most hosts.Any Fetch runtimeA Request to Response handler for Node, Bun, Deno or Workers. The starter needs Node 24.DocsRust binaryAxum or Actix server plus a WASM bundle.Rust binaryServer, WASM, desktop or mobile.
Ecosystem
EcosystemNewSmall so far. fsr use vendors npm packages into the app, with no node_modules.LargestIntegrations, hosting and hiring.LargeA module for most needs.GrowingSmaller than React's and well liked.Growing fastCMS and content integrations.npm, minus ReactBrowser npm packages and web components work. React libraries do not.DocsSmallRust crates, few UI libraries.SmallRust crates, few UI libraries.
Difficulty
DifficultyMediumRoutes, loaders and actions in TypeScript. Islands and the shared store are new ideas.MediumThe App Router has a lot to learn.Low to mediumConventions do most of the wiring.LowSyntax close to HTML.Low to mediumA simple core.New modelOne dependency to start. Components, frames and routing are all new APIs.DocsHighRust, lifetimes and signals.Medium to highRust with React-like components.

Performance: one product page rendered by each engine on one machine, from the benchmarks page. The other columns summarise each project's own documentation, with a link where a row's argument depends on the claim, checked in October 2026.