# SPA mode & error pages

Serve /index.html for unmatched routes so client-side routers work, and map status codes to your own pages.

Two options cover the common routing cases for static sites.

## Single-page apps

Add `--spa` and any path that doesn't match a file is served `/index.html` with
a `200` — so client-side routers (React Router, Vue Router, TanStack Router)
handle deep links instead of returning a 404.

```console title="spa"
$ one9x pages release ./dist --site myapp --spa --deploy
✓ Live · unknown routes → /index.html
```

This is the flag that decides whether an app *works*. Without it, the first
visitor who hard-refreshes on `/settings` or follows a shared deep link gets a
404 — the app is fine, the routing never got a chance to run.

You need it for a client-rendered app (Vite + React Router, Vue Router, Create
React App). You don't need it for a static site generator that writes a real
HTML file per route — Astro, Hugo, Eleventy, Next.js `output: 'export'` — where
an unmatched path genuinely is a 404 and should say so. [Frameworks](/docs/frameworks)
has the per-framework answer.

> `--spa` and `--index` are the same slot and conflict. `--index` names a
> different fallback document; `--spa` is the shorthand for `--index
> /index.html`. Pass one or the other.

## Custom error pages

Map a status code to a page in your build with `--error code:path`. The flag
repeats, so you can set as many as you need.

```console title="error pages"
$ one9x pages release ./dist --site mysite --deploy \
  --error 404:/404.html \
  --error 403:/403.html
✓ Live · custom 404, 403
```

The path is a path *within the release* — it has to be a file the build actually
produced, at the exact path it lives at. `--error 404:/404.html` with the file
at `/404/index.html` maps to nothing.

Error pages and `--spa` interact the way you would want: with `--spa` on, an
unmatched route is not an error at all, so the 404 page is only reached when the
app itself asks for it.