Skip to content
forma.
v3.0
☰
Browse documentation
Start here
Build interfaces
Native rendering
Tooling
Security and operations
Architecture
Application guides
API reference

Docs/Application guides

FORMA 3.0

Configure an application

Choose browser or native rendering, declare routes and keep build inputs aligned.

View source ↗Markdown ↓Site guide · Forma 3.0
On this page

Start by choosing where the initial HTML comes from. A browser application can use @getforma/core with a JavaScript bundler. Native rendering additionally uses @getforma/compiler, @getforma/build, forma-ir and forma-server. The official packages used by this site are pinned to 3.0.0.

Choose the rendering mode explicitly#

The minimal template has two build commands. Its ordinary build creates Browser routes; the SSR command compiles native pages. From a generated application's root:

sh
cd admin
npm ci
npm run build:ssr
cd ..
CARGO_BUILD_JOBS=3 cargo run --locked

npm ci installs the locked frontend dependencies; build:ssr emits native FMIR and its paired browser assets. The Cargo command then builds and starts the Rust server with those files. Run the steps in this order after changing page source.

The template listens on localhost:3000 by default. Request the page with a browser and inspect its source: the server-rendered counter starts at 7 before the browser bundle runs. The template's Rust handler supplies that 7. Clicking changes only the current page's signal; refreshing requests 7 again because no save exists. A Browser build starts its separate client counter at 0. Changing a build flag is a change to rendering behavior, not just an optimization.

Keep the page owner and route aligned#

This excerpt shows the Home portion of a native build, using the minimal template's paths. Keep the template's other entries, such as About, in your actual configuration. Paths are relative to the build working directory.

js
import { build } from "@getforma/build";

await build({
  app: "my-forma-app",
  entryPoints: [{ entry: "src/home/app.tsx", outfile: "home.js" }],
  cssEntries: [{ input: "src/styles/app.css", outfile: "app.css" }],
  routes: { "/": { js: ["home"], css: ["app"] } },
  outputDir: "dist",
  ssr: true,
  ssrEntryPoints: { home: "src/home/app.tsx" },
  diagnosticsJson: "ssr-diagnostics.json",
});

The home owner connects the browser entry, native entry and route. It also names the Rust handle: pages.bind::<HomeProps>("home"). It is not a URL. Keep the owner in the input route even if the page has no islands: the build removes unnecessary JavaScript from the output itself. An input with js: [] loses that owner association.

Declare request data at the boundary#

Declare page props in TypeScript and derive matching PageProps in Rust. Validate request data before creating those props. Load the manifest, require the routes you expect to be SSR, load modules, bind all handles and call finalize() before opening the listener. Then return the complete PageResponse for each request. The result should already contain the requested values in its HTML; an island then adds browser interaction. Persistence requires a separate validated write path, as demonstrated by Field Notes.

The declared props guide explains supported shapes. The native subset defines which expressions the compiler can represent. Move arbitrary business calculations into Rust and pass explicit presentation values when a native expression is unsupported.

Separate public configuration from secrets#

Browser bundles and rendered props are visible to visitors. Database credentials, signing keys and other server secrets belong in the server process environment, not page props or build substitutions. Environment variable names belong to your application; Forma does not turn every environment variable into a public setting.

The website's native origin uses HOST, PORT and SITE_DIST. The published minimal template uses FORMA_PORT. Use the names implemented by the application you run.

Diagnose the boundary that failed#

SymptomFirst evidence to inspect
Native build refusedDiagnostic code and original source location; check the admitted subset
Server refuses startupRequired SSR route, matching manifest/modules, props schema and finalize result
Initial data is wrongRaw HTTP response and validated request props
Island does not respondBrowser errors, response policy, paired bundle and actual input behavior
Open tabs break after deploymentBuild identity and retained assets; never paste a new bundle over an old manifest

Keep emitted diagnostics with the build. Unsupported native source always fails in 3.0; strict: false does not enable a silent browser fallback. See the build reference for the complete configuration.

Find your next answer.

Search Forma 3.0 documentation, guides and examples.