# 300. The parts bin

**For:** app developers. Task-shaped answers, each one a block to lift.

## I want a page at a URL

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 `params.id`.

## I want that page to have data

Put a `page.loader.ts` beside it exporting `load`. Its return type becomes the page's props type, generated into `@generated/client` under the route's name.

## I want a title and a description

Export `meta` from the same loader.

```ts
export const meta = ({ data }: { data: { name: string } }) => ({
  title: `${data.name} · Storefront`,
  description: `Buy ${data.name}.`,
  head: [og("type", "product"), canonical(`/product/${data.slug}`)],
});
```

## I want a header on every page

Add `routes/layout.tsx` with a `routes/layout.loader.ts` beside it. If the loader reads no route parameters, the layout survives every navigation beneath it with its DOM and island state intact.

## I want part of the page to stream

Add `loading.tsx` beside the page. The file's presence declares it. Nothing in the page or the loader changes.

## I want a button that does something

Put an `actions.ts` beside the page that exports a declared `action`. Call it from an island through the generated `actions` handle. It re-runs the affected loaders on success.

## I want to guard a route

Use middleware or a guard at the top of the loader or action. A guard that reads nothing external runs before any socket opens.

## I want state the server keeps

Use a server island: `<Island when="visible" mode="server">`. The browser ships no component code and every interaction is a round trip.

## I want state two islands share

Use the store. Seed it from a loader's `store` export and read it with a typed `key`. Prefix your keys, since the store is one flat map.

## I want a third-party package

```
fsr add app <name>@<version>
```

It vendors an ESM bundle under `app/vendor/`, commits it and adds the import-map entry. Nothing is fetched at deploy time.

## I want a font, a favicon or a meta tag on every page

Use the root layout's `meta`. The helpers in `@snapfire/fsr/head` cover the common rows and an object literal with a `tag` covers the rest. An inner route overrides one row by naming the same element again.

```ts
export const meta = () => ({
  head: [
    linkTag("preconnect", "https://fonts.example"),
    linkTag("stylesheet", "https://fonts.example/inter/inter.css"),
    { tag: "link", rel: "icon", type: "image/x-icon", href: "/static/favicon.ico" },
  ],
});
```

## I want a script on every page

Write a module that `app/src/main.ts` imports for its effect. There is no head row for a script. A library comes in through `fsr add` and the entry imports it. A vendor that must load as a script element is a module that creates that element when the page needs it.

## I want Google Analytics behind a consent banner

Use two modules. One runs the banner and calls the other from its consent callback. The other builds the `dataLayer`, pushes `arguments` the way Google's snippet does and appends the gtag.js script. The measurement id is a `[public]` value the root layout's `store` seeds the browser with. Tutorial [095](https://snapfirers.com/fsr/learn/340-add-google-analytics-with-a-consent-banner.md) builds it end to end.

## I want a value that differs per deployment

Put it in `[public]` in `config/app.toml`, override it in a deployment's overlay and read it in a loader as `ctx.config.<key>`. The declaration types the read, so a misspelt key is a build error. The value reaches the browser through whatever the loader returns, so a secret does not belong there.

A secret goes in the same files as a c5store `.c5encval`: the decryptor, the key name and the ciphertext, which `c5cli` writes into TOML, YAML or JSON. The host decrypts it while loading, with the key of that name from `config/private_keys` or from `C5_SECRETKEY_<NAME>`. `Loader::secrets` is where a Rust host changes that.

## I want to call a backend

Declare a client in `config/app.toml` and put its contract in `app/clients/`. It arrives in every loader and action as `services.<name>`, typed from the contract. Use `transport = "mock"` with a `.mock.json` beside it to develop and test against it.

## I want to call my own Rust

Mark an `impl` block and register it. The loader stays where it is and only the computation moves.

```rust
#[native]
impl Digest {
  pub fn words(&self, bodies: Vec<String>) -> i64 {
    bodies.iter().map(|b| b.split_whitespace().count() as i64).sum()
  }
}
```

```rust
Host::from(...).native("digest", Arc::new(Digest))
```

```ts
const words = native.digest.words({ bodies });
```

Rust is synchronous and so is this, so `words` returns a number rather than a promise. Only what the block declares `pub` crosses, so a module holding another calls it as an ordinary method and that one never appears in TypeScript. There is no contract, no transport and no interceptor chain, because nothing is crossing a boundary: the declaration comes from the signature, which `fsr` reads with `syn` before anything compiles, so the two cannot drift.

Use a service instead when the thing really is a service, meaning something over a wire that a document already describes. Also use one when you want the cache and the interceptors that come with a call that crosses.

A service with no document behind it is declared the same way, with `#[service]` in place of `#[native]`. The attribute writes both the transport and the contract off the signatures, so the call crosses the boundary properly: arguments and answers are checked, interceptors run, a `#[cache]` policy is honoured and a `Result<T, ServiceError>` arrives as the failure it names. A method taking a `Caller` is told who is calling without the body passing anything. `caller.require()?` refuses an anonymous call. The build writes the contract to `generated/contracts/rust.json`; the host refuses a disagreement at boot, so a build that fell behind the Rust fails on startup rather than on a call.

## I want to test without a browser

Run `fsr test <app>`. Body tests replay a loader against mocked services and assert on what it returned. Page tests render over a DOM. Route tests go through the host. None of them need Node.

## I want search engines to see one URL per page

Name the address the deployment is reached at. The host then writes every `rel=canonical` and `rel=alternate` absolute:

```toml
[document]
origin = "https://example.com"
```

A crawler reads those two as absolute URLs only, so a path on either is an ignored tag rather than a weaker one. The value is the scheme and the host with nothing after it. A trailing slash or a path is refused at boot. Use one preferred origin for the deployment instead of the host a request arrived on, because a canonical link exists to collapse two names serving the same pages.

## I want to know which host a request came in on

Read `ctx.host` in a loader or an action, once `[server] hosts` names the hosts the deployment answers on. The list is an allowlist rather than a format. A `Host` it does not hold gives `null` instead of the value the client sent. With the key unset the header is never read. Whatever sits in front of the host has to set the header for the value to mean anything, which is `proxy_set_header Host $host;` under a matching `server_name` in nginx.

## I want to know what the host actually did

Read the boot report with `fsr serve --verbose`. It lists every route with its pattern, every loader with whether it lowered, every component with whether the server can render it, every static root and everything ignored. When something does not work, the report has usually already said so.

## I want to see what my TypeScript became

The [IR inspector](https://snapfirers.com/fsr) shows real sources beside the exact IR the lowerer produced, including one that is deliberately refused.
