Static sites

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.

sluurp static --public website --public todos=examples/todos/app --out dist

It takes --public just like serve (a folder or a repository URL), 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:

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

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