---
title: Static sites
description: Publish an app as plain files, for GitHub Pages or any file host, with its islands still alive.
section: Operations
order: 4
---

# Static sites

<p class="lead">If a site doesn't need a server (landing pages and docs usually don't), Sluurp can export it as static files. Pages are pre-rendered and islands still hydrate in the browser. This site is published that way.</p>

```sh title="Terminal"
sluurp static --public website --public todos=examples/todos/app --out dist
```

It takes `--public` just like `serve` (a folder or a [repository URL](/docs/getting-started)), runs the app locally and crawls it. Starting from `/` and each mounted app, it follows every link, script, stylesheet, island, import-map entry, module import and font, and writes out:

- pages as `path/index.html`, so `/docs/sync` becomes `docs/sync/index.html`;
- everything else at its own path, with TypeScript modules compiled and saved as `.js` so any host serves them with the right type.

## What works

Everything rendered on the server comes through unchanged: pages, Markdown docs, highlighted code, the UI kit. Client-only islands keep working: charts, forms, a Page editor, the chart playground.

Anything that needs a server doesn't: the API, sync, `"use server"` functions and live cursors. Islands that call the server still load, but get no response. The Todos Collab and Sheet examples on this site are like that.

## GitHub Pages

The output includes a `.nojekyll` file so GitHub publishes `/_/` (theme and styles) instead of Jekyll dropping it for starting with an underscore. A workflow to publish on every push:

```yaml title=".github/workflows/pages.yml"
name: Pages
on:
  push:
    branches: [master]
permissions:
  contents: read
  pages: write
  id-token: write
jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: github-pages
    steps:
      - uses: actions/checkout@v4
      - run: curl -fsSL https://raw.githubusercontent.com/SluurpHQ/releases/main/install.sh | sh
      - run: ~/.sluurp/bin/sluurp static --public website --out dist
      - uses: actions/upload-pages-artifact@v3
        with:
          path: dist
      - uses: actions/deploy-pages@v4
```

## Served from a folder

A project's GitHub Pages site lives under the repository name: `username.github.io/repo/`. Pass it as `--base`:

```sh title="Terminal"
sluurp static --public website --out dist --base repo
```

Root-relative URLs in pages and stylesheets (`/docs`, `/_/theme.css`) are rewritten under `/repo/`, and each page's import map is adjusted too, so module imports and named islands resolve under the base without rewriting any module. On a custom domain or a `username.github.io` repository the site is at the root and needs no `--base`.

Root paths built from strings in your own code, like `link.href = "/theme.css"`, aren't rewritten. Resolve them relative to the module instead: `new URL("../theme.css", import.meta.url)`.
