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

Pure SPA (template: **frontend-spa**) — a fully client-rendered tool
with no backend and no SSR.

## Boot

```bash
pnpm install
pnpm --filter @{{projectName}}/{{appName}} dev
```

## When to reach for `renderMode: 'spa'`

Use it when the page is heavily interactive AND its state lives in the
browser, so a server-rendered first paint would just be discarded on
hydration. This template's bill splitter is the canonical case: every
input is interactive and the last tip % is remembered in `localStorage`
— there is nothing meaningful for the server to render.

For content pages (marketing, blog, docs) prefer `renderMode: 'static'`
and reach for `island()` when only a few widgets are interactive — see
the **frontend-static-blog** template. Use `spa` only when the JS *is*
the page.

## What this template demonstrates

| Concept | Where |
|---|---|
| `renderMode = 'spa'` (client-render, no pre-render) | `src/pages/index.tsx` |
| Default `interactive: 'full'` (whole tree hydrates) | `src/pages/index.tsx` |
| Browser-only state via `localStorage` (read in an effect) | `src/pages/index.tsx` |
| Zero backend — no rpc client, no loader | the whole app |

## Build + serve

```bash
pnpm --filter @{{projectName}}/{{appName}} build
pnpm --filter @{{projectName}}/{{appName}} start
# or push the static dist/ to a CDN:
voltro static deploy --host cloudflare-pages --project-name {{appName}} --spa
```

The `--spa` flag serves `index.html` for unknown routes — the correct
fallback for a single-page app on a CDN.
