# SvelteKit

adapter-static is required — adapter-auto produces a server build. Output lands in build/.

SvelteKit ships with `adapter-auto`, which picks a **server** target based on
where it thinks it is being deployed. That produces a build Pages cannot serve.
Swap it for `adapter-static`.

```console title="install the adapter"
$ npm i -D @sveltejs/adapter-static
```

```js
// svelte.config.js
import adapter from '@sveltejs/adapter-static';

export default {
  kit: {
    adapter: adapter(),
  },
};
```

```console title="sveltekit"
$ npm run build
vite v7.0.0 building SSR bundle for production...
✓ Wrote site to "build"
$ one9x pages release ./build --site mysite --deploy
✓ v1 live
→ https://mysite.one9x.app/
```

## Prerendered, or SPA?

This is the one decision, and it changes whether you pass `--spa`.

**Prerendered (the default with `adapter-static`).** Add both of these to
`src/routes/+layout.js`:

```js
export const prerender = true;
export const trailingSlash = 'always';
```

`prerender` is what makes the adapter write anything at all. `trailingSlash` is
**required**, not a preference: without it the adapter writes `build/about.html`,
and Pages serves paths rather than routes — so a request for `/about` 404s. With
it, every route becomes `build/about/index.html` and resolves. The symptom if you
skip it is a homepage that works and internal links that all 404.

**Do not pass `--spa`** — a genuine 404 should be a 404.

**SPA fallback.** For an app whose routes can't be prerendered (anything behind
a login, anything keyed on runtime data), give the adapter a fallback page:

```js
adapter({ fallback: 'index.html' })
```

Then deploy with `--spa`, so unmatched paths serve that fallback:

```console title="spa fallback"
$ one9x pages release ./build --site myapp --spa --deploy
✓ v1 live · unknown routes → /index.html
```

You can mix the two — prerender the marketing routes, fall back for the app —
but if any route relies on the fallback, you need `--spa`.

## Server endpoints

`+page.server.js`, `+server.js` and form actions need a running server. With
`adapter-static` the build fails naming the route, which is the right outcome:
it tells you at build time rather than at 3am.

## Start from a template

Live, and deployed by the workflow in that repo on every push:
**[sveltekit.one9x.app](https://sveltekit.one9x.app/)**.

[`one9x/template-sveltekit`](https://github.com/one9x/template-sveltekit) is a minimal SvelteKit starter
with this deploy already wired up. Press **Use this template** on GitHub to get
your own copy, or read it as a reference for a project you already have.

Set two things in the new repo under **Settings → Secrets and variables →
Actions** and it deploys on every push:

| | Name | Value |
| --- | --- | --- |
| Variable | `ONE9X_SITE` | the site to deploy to |
| Secret | `ONE9X_TOKEN` | `one9x tokens create "github actions"` |

Until `ONE9X_SITE` is set the workflow is skipped rather than failed — a fresh
copy should not open with a red X.

## Deploying on every push

```yaml
- uses: actions/setup-node@v5
  with: { node-version: 22, cache: npm }
- run: npm ci
- run: npm run build
- uses: one9x/pages-deploy@v1
  with:
    site: mysite
    dir: build
    token: ${{ secrets.ONE9X_TOKEN }}
    deploy: ${{ github.ref == 'refs/heads/main' }}
```

Add `spa: true` if you are using the fallback.