# Configure an application

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](/docs/3.0/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](/learn/full-stack/).

The [declared props guide](/docs/3.0/declared-props/) explains supported shapes.
The [native subset](/docs/3.0/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

| Symptom                          | First evidence to inspect                                                         |
| -------------------------------- | --------------------------------------------------------------------------------- |
| Native build refused             | Diagnostic code and original source location; check the admitted subset           |
| Server refuses startup           | Required SSR route, matching manifest/modules, props schema and finalize result   |
| Initial data is wrong            | Raw HTTP response and validated request props                                     |
| Island does not respond          | Browser errors, response policy, paired bundle and actual input behavior          |
| Open tabs break after deployment | Build 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](/docs/3.0/build/) for the complete configuration.
