# {{projectName}} / {{appName}}

Voltro web app scaffold (template: **frontend-ssr-api**) — server-rendered
pages fed by a sibling api.

## Boot — needs a paired api

This template consumes an api that exposes `notes.list` / `notes.create`.
Scaffold both:

```bash
voltro create-project acme --api=api-backend --web=frontend-ssr-api
pnpm install
# api in one terminal, web in another:
pnpm --filter @acme/api dev
pnpm --filter @acme/web dev      # → http://localhost:{{port}}
```

## What this shows

- **SSR fed by your backend** — `src/pages/index.tsx` is `renderMode: 'ssr'`.
  Its loader runs on the server on every request and calls
  `query('notes.list', {})` over the api's `POST /rpc` surface (forwarding the
  session cookie, so the same Subject + tenant resolve as the WebSocket path).
  Real rows are in the **first paint** and the document `<title>` — crawlable,
  no client round-trip.
- **`meta` from loader data** — the `<title>` reflects the real note count in
  the server-rendered HTML (good for SEO + social cards), not a client guess.
- **SSR → live, one data source** — the component renders the SSR'd snapshot,
  then `useSubscription('app', 'notes.list')` upgrades it to live once the
  WebSocket connects (`useSubscription` is `undefined` during SSR, so the
  first paint is the loader's data; it swaps to live on the client).

## How the pairing resolves

`apis.app.package` in `app.config.ts` points at the sibling api package
(`@{{projectName}}/api`). The api's port is auto-discovered from its own
`app.config.ts`, and codegen pulls its typed rpc surface — so `query` and
`useSubscription` are end-to-end typed against the api's schema.

## SSR vs the other render modes

- This page must hit the api per request → `renderMode: 'ssr'`.
- If the data changes only occasionally and can be cached, use `'isr'`
  (`export const revalidate = '5 minutes'`) — same loader, cached HTML.
- For a fully client-reactive page (no SSR), use `useSubscription` /
  `useMutation` in a default-mode page — see `frontend-app`.

See AGENTS.md › *Server-side rpc in loaders (`ctx.query`)* for the full
reference.
