documentation menu

SvelteKit

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

read as markdown

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.

install the adapter
$ npm i -D @sveltejs/adapter-static
// svelte.config.js
import adapter from '@sveltejs/adapter-static';

export default {
  kit: {
    adapter: adapter(),
  },
};
sveltekit
$ npm run buildvite 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:

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:

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

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

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.

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:

NameValue
VariableONE9X_SITEthe site to deploy to
SecretONE9X_TOKENone9x 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

- 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.