Server-rendered sites

The server shell is for web experiences that need more than static HTML but should still keep the advantages of Fission's widget model. It is useful for ecommerce stores, dashboards, catalogs, account pages, portals, documentation with dynamic sections, and any route that needs request-time data, per-session state, revalidation, or server-side actions.
The Pokémon Card Store example under examples/pokemon-card-store is the reference project. It renders a storefront from normal Fission widgets, keeps the cart in a session-private route, drains a server job before responding, declares a progressive worker for browser-side enhancement, declares a focused WASM island for the cart drawer, and can be packaged into a Docker image. Revalidated routes use the same server shell, but a page that emits signed action forms is deliberately private or request-rendered unless you move that interaction into an island.

1. Enable the SSR target

A server project declares ssr as a target and points [server].entry at the Rust function that creates the server app.
targets = ["ssr"]

[app]
name = "pokemon-card-store"
app_id = "rs.fission.examples.pokemon_card_store"

[server]
entry = "pokemon_card_store::pokemon_card_store_server"
default_route_mode = "server_private"
render_pass_limit = 4

What you should have working

After this guide, you should be able to explain where Server-rendered sites fits in the Fission lifecycle, identify the file or component you changed, run the relevant fission command, and verify the result in a real target or test.

Common mistakes to check

Symptom
What to check first
The app compiles but nothing changes
Confirm the component is actually used by the route or shell target you are running.
An action fires but state does not update
Confirm the reducer is registered with the same action value that the UI dispatches.
A platform feature reports unsupported
Confirm the target has the capability in fission.toml and the shell registered the provider.
The page works on one target but not another
Run fission doctor, then inspect target-specific configuration and feature flags.
Tests are hard to write
Split the product rule into a reducer test first, then add a UI or shell smoke test for the integration.
The entry function is used by CLI commands such as fission server check, fission server serve, fission server artifacts, and Docker packaging.

2. Build the server app

The server app is assembled in normal Rust. This abbreviated version mirrors the structure in examples/pokemon-card-store/src/server.rs.
use fission::server::{
    FissionServerApp, ProgressiveWorker, ServerJobRegistry, ServerPrivatePolicy,
    WasmIsland, WebRouteMode,
};
pub fn pokemon_card_store_server() -> FissionServerApp {
    FissionServerApp::new("Pokemon Card Store")
        .jobs(ServerJobRegistry::new().register_job(CATALOG_JOB, |_request, _ctx| {
            Ok(catalog_response())
        }))
        .route_widget::<StoreState, _>(
            "/",
            "Pokemon Card Store",
            Some("A Fission server-rendered storefront for collectible cards.".to_string()),
            WebRouteMode::ServerPrivate(ServerPrivatePolicy::default()),
            StoreHomePage,
        )
        .worker(
            "/",
            ProgressiveWorker::new("catalog-filters", "/assets/workers/catalog-filters.wasm")
                .entry("pokemon_card_store::workers::catalog_filters_boot")
                .root_node_id("catalog-grid"),
        )
        .island(
            "/",
            WasmIsland::new("cart-drawer", "/assets/islands/cart-drawer.wasm", "cart-drawer")
                .entry("pokemon_card_store::islands::cart_drawer_boot"),
        )
}
The route widget remains the page's source of truth. Jobs describe when data is prepared before HTML is returned. Route mode describes whether the rendered page is public, revalidated, request-rendered, or session-private. Workers and islands describe browser-side enhancement without turning the whole page into one large browser app.
Server routes use the same :param segment syntax as the widget router. A route such as /cards/:slug receives extracted parameters in ServerRenderContext::route_params, so the initial state loader can fetch the correct product before the widget tree is built:
.route_widget_with_state::<StoreState, _, _>(
    "/cards/:slug",
    "Card detail",
    None,
    WebRouteMode::ServerPrivate(ServerPrivatePolicy::default()),
    CardDetailPage,
    |ctx| {
        let slug = ctx.route_params["slug"].clone();
        Ok(StoreState::for_card(ctx.session.id(), slug))
    },
)
Exact routes take precedence over dynamic routes, so /cards/new can still be registered as its own route before /cards/:slug.

3. Use a normal CLI entrypoint

The binary delegates to the server shell's CLI runner.
use fission::server::run_from_cli;
use pokemon_card_store::pokemon_card_store_server;

fn main() -> anyhow::Result<()> {
    run_from_cli(pokemon_card_store_server())
}
That one entrypoint supports check, routes, serve, build, and artifacts subcommands. Fission's top-level CLI calls those subcommands for you.

4. Check, serve, and generate browser artifacts

Render all declared routes:
fission server check --project-dir examples/pokemon-card-store
Serve locally:
fission server serve --project-dir examples/pokemon-card-store --host 127.0.0.1 --port 8124
Generate and compile worker and island browser artifacts:
fission server artifacts --project-dir examples/pokemon-card-store
Run these before packaging. A server route can fail because a widget cannot render server-side, a job is missing, a worker entry is invalid, or an island cannot produce a browser artifact. check renders routes and builds browser artifacts; artifacts is useful when you only want to regenerate worker and island WASM output.

5. Package the server as a Docker image

Server apps usually deploy as processes. Fission's Docker package format creates a context that builds the server app inside a Rust builder image and runs the compiled binary in a runtime image.
[package.docker]
port = 8080
tags = ["ghcr.io/example/pokemon-card-store:0.1.0"]
fission package \
  --project-dir examples/pokemon-card-store \
  --target ssr \
  --format docker-image \
  --release
The generated image runs the server app with serve --host 0.0.0.0 --port $PORT. The artifact manifest records the Dockerfile, image metadata, generated context, configured tags, and validation checks.
For the security model, session settings, action-origin checks, and HTTP adapter hooks, continue to Server security.
Fission
A cross-platform, GPU-accelerated user interface framework for Rust. MIT licensed.
Copyright (c) 2026 Fission
Ready to use today. Widget APIs are expected to remain stable; some runtime and shell APIs may change before 1.0.0.
Fission 0.7.0