# Static Deployment

The `static` runtime declaration generates a client-only bundle with no server code. This is for single-page applications (SPAs) that do not need server-side logic at runtime.

## Build

```bash
alepha build --runtime=static
```

`static` anywhere in `build.runtime` makes the whole build static, whatever else the list names. The build process:

1. Builds the client. No server bundle is built: the build analyzes the app in-process instead, to learn its pages.
2. Prerenders every `static: true` page, every `$route({ static: true })` and every `$sitemap` not set to `static: false`, as an SSR build does.
3. Writes `index.html`. A `/` page prerendered in step 2 keeps its HTML; otherwise the `$page` at `/` (one declaring `path: "/"` or leaving `path` out, and with no children) is rendered and stripped to an empty `<div id="root"></div>`, and an app with none fails here.
4. Writes `404.html` and `200.html`, SPA shells of `index.html`, and a `CNAME` file for Surge. Each is written only if your `public/` did not already provide it.
5. Removes everything but `public/` and the manifest.
6. Writes [`_headers`](https://latest.alepha.dev/docs/guides-deployment-headers.md) and a brotli `.br` copy of each compressible file.

`--prebuilt` is refused: a static build needs the live app to render its pages.

## Output Structure

```txt
dist/
  public/
    index.html      # Prerendered `/` page, or the SPA shell
    200.html         # SPA fallback
    404.html         # Not-found fallback
    CNAME            # Surge domain
    _headers         # Cache and security headers, for hosts that read it
    entry.*.js, chunk.*.js, asset.*   # JS, CSS, images, at the root
    *.br             # Brotli copies, for hosts that serve them
  manifest.json      # What the build declared
```

## Deploy

The output in `dist/public/` is plain HTML, CSS and JS: deploy it to any static host. With [Surge](https://surge.sh) (which the generated `CNAME` file targets):

```bash
alepha build --runtime=static
npx surge dist/public
```

Cloudflare Pages, Netlify, or GitHub Pages work just as well: point them at `dist/public/`. Cloudflare Pages and Netlify read the `_headers` file as is.

## Configure Domain

Set a custom Surge domain in `alepha.config.ts`:

```typescript
import { defineConfig } from "alepha/cli/config";

export default defineConfig({
  build: {
    runtime: ["static"],
    static: {
      domain: "myapp.surge.sh",
    },
  },
});
```

If no domain is specified, Alepha generates a deterministic domain from your `package.json` name: `{name}-{hash}.surge.sh`.

## Shipping a Client Built by Something Else

The steps above assume Alepha rendered the site: its own Vite client build, or a `$page` at `/`. A site built by anything else - a hand-written `index.html` through plain Vite, a static export from another tool - has no page for the build to render, and filling `dist/public/` yourself does not help either, because the build cleans `dist/` before any task runs.

Point `build.static.source` at the directory your own build fills instead:

```typescript
import { defineConfig } from "alepha/cli/config";

export default defineConfig({
  build: {
    runtime: ["static"],
    static: {
      source: "dist-client",
    },
  },
});
```

```bash
vite build --outDir dist-client --emptyOutDir
alepha build --runtime=static
```

The directory is copied into `dist/public/` before the fallbacks are derived, so your `index.html` ships as written and `200.html`/`404.html` are stripped-down shells of it. Two rules:

- **It must live outside `dist/`.** A source inside it is refused by name - the clean step deletes it before it can be read.
- **It must contain an `index.html`.** Otherwise a static host has nothing to answer `/` with, and the build says so rather than failing later on a path you never wrote.

A server entry is still required, because the build boots the workspace to analyze it. Nothing of it ships - the static build keeps only the client directory and the manifest - so a bare `run(Alepha.create())` is enough for a site with no server of its own.

## Other Hosting Providers

The `dist/public/` directory is a standard static site. Deploy it to any static hosting:

- **GitHub Pages**: copy `dist/public/` to the `gh-pages` branch
- **Netlify**: set build output to `dist/public`
- **Cloudflare Pages**: set build output to `dist/public`

The `200.html` file serves as the SPA fallback for hosting providers that support it (Surge, Netlify). Configure your hosting provider's rewrite rules to serve `200.html` for all unmatched routes.

## Sitemap

Add the `$sitemap` primitive to a router. It is prerendered to
`dist/public/sitemap.xml` alongside the static build:

```typescript
import { $sitemap } from "alepha/react/sitemap";

class AppRouter {
  // hostname defaults to PUBLIC_URL, then "" (relative URLs)
  sitemap = $sitemap({ hostname: "https://myapp.com" });
}
```

The sitemap lists every leaf `$page` with no parameter, `static` or not, and the
`static.entries` of a page with parameters, excluding wildcard paths and `/404`. See
[Sitemap](https://latest.alepha.dev/docs/guides-frontend-sitemap.md#which-pages-it-lists).
