DEV Community

Julia for Light Cloud

Posted on Originally published at blog.light-cloud.com on

Deploy a SvelteKit Site as Static Files: adapter-static and Prerendering

Deploy a SvelteKit Site as Static Files: adapter-static and Prerendering

To deploy a SvelteKit site on Light Cloud as static files, create it with adapter-static, add export const prerender = true to src/routes/+layout.ts, push it to GitHub and click Deploy in the console. Light Cloud detects SvelteKit and adapter-static, runs npm run build and serves the build folder from the edge. Each page is a real HTML file, so /about opened directly shows the About page.

In my run the deploy took 30 seconds.

What you will build

A two-page SvelteKit site, Home and About, prerendered to HTML at build time and live on a light-cloud.io address. It follows the visitor's light or dark system setting.

The finished SvelteKit site served by Light Cloud: Home and About links above the headline Hello from Light Cloud

Live demo: main-tutorial-sveltekit-app-examples.light-cloud.io. Source code: github.com/light-cloud-com/tutorial-sveltekit-app.

Static or server: which adapter?

SvelteKit builds for a target through an adapter. The two that matter here:

  • adapter-static builds every page to HTML ahead of time. Nothing runs on a server; the files are served from the edge. Good for sites, docs, blogs and portfolios, and it runs on the free plan. This guide uses it.
  • adapter-node builds a Node.js server that renders pages on each request. You need it for form actions, server load functions that read per-visitor data, or API routes. On Light Cloud that runs as a container.

If you are not sure, start static. Moving to adapter-node later is a change of adapter, not a rewrite.

Before you start

Step 1: Create the SvelteKit app

The Svelte CLI, sv, can add adapter-static while it creates the project:

$ npx sv@latest create tutorial-sveltekit-app --template minimal --types ts --add sveltekit-adapter="adapter:static" --install npm
┌ Welcome to the Svelte CLI! (v0.17.1)
│
◆ Project created
│
◆ Successfully setup add-ons: sveltekit-adapter
│
◆ Successfully installed dependencies with npm
│
└ You're all set!


PS> npx sv@latest create tutorial-sveltekit-app --template minimal --types ts --add sveltekit-adapter="adapter:static" --install npm
┌ Welcome to the Svelte CLI! (v0.17.1)
│
◆ Project created
│
◆ Successfully setup add-ons: sveltekit-adapter
│
◆ Successfully installed dependencies with npm
│
└ You're all set!

Enter fullscreen mode Exit fullscreen mode

--template minimal gives an almost empty site and --types ts sets up TypeScript. Output is trimmed.

Looking for svelte.config.js? New projects no longer have one. The adapter is set in vite.config.ts:

import adapter from '@sveltejs/adapter-static';
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';

export default defineConfig({
    plugins: [
        sveltekit({
            compilerOptions: {
                // Force runes mode for the project, except for libraries. Can be removed in svelte 6.
                runes: ({ filename }) => filename.split(/[/\\]/).includes('node_modules') ? undefined : true
            },
            adapter: adapter()
        })
    ]
});

Enter fullscreen mode Exit fullscreen mode

Step 2: Turn on prerendering

adapter-static can only publish pages that were built ahead of time, and SvelteKit does not prerender by default. Build the new project as it is and it stops with Error: Encountered dynamic routes (the full message is under Troubleshooting).

One line fixes it for the whole site. Create this file:

export const prerender = true;

Enter fullscreen mode Exit fullscreen mode

Step 3: Add the pages

A shared layout with two links and a few styles. color-scheme: light dark lets the browser follow the visitor's system theme:

<script lang="ts">
    import { page } from '$app/state';
    import favicon from '$lib/assets/favicon.svg';

    let { children } = $props();
</script>

<svelte:head>
    <link rel="icon" href={favicon} />
</svelte:head>

<nav>
    <a href="https://blog.light-cloud.com/" aria-current={page.url.pathname === '/' ? 'page' : undefined}>Home</a>
    <a href="https://blog.light-cloud.com/about" aria-current={page.url.pathname === '/about' ? 'page' : undefined}>About</a>
</nav>

<main>
    {@render children()}
</main>

<style>
    :global(:root) {
        color-scheme: light dark;
        font-family: system-ui, sans-serif;
    }

    :global(body) {
        max-width: 40rem;
        margin: 0 auto;
        padding: 2rem 1rem;
    }

    nav {
        display: flex;
        gap: 1rem;
        margin-bottom: 2rem;
    }

    a {
        color: light-dark(#d93a00, #ff8a4c);
    }

    a[aria-current='page'] {
        font-weight: 600;
        text-decoration: none;
    }
</style>

Enter fullscreen mode Exit fullscreen mode

Replace the starter home page:

<svelte:head>
    <title>Home</title>
</svelte:head>

<h1>Hello from Light Cloud</h1>
<p>A SvelteKit site, prerendered with adapter-static.</p>

Enter fullscreen mode Exit fullscreen mode

In SvelteKit a folder is a route, so the About page lives in an about folder:

<svelte:head>
    <title>About</title>
</svelte:head>

<h1>About</h1>
<p>This page was prerendered to HTML at build time and is served from the edge.</p>

Enter fullscreen mode Exit fullscreen mode

Step 4: Run it and build it

$ npm run dev
  VITE v8.3.1 ready in 441 ms

  ➜ Local: http://localhost:5173/
$ npm run build
✓ built in 974ms
> Using @sveltejs/adapter-static
  Wrote site to "build"
  ✔ done
$ ls build
_app
about.html
index.html
robots.txt


PS> npm run dev
  VITE v8.3.1 ready in 441 ms

  ➜ Local: http://localhost:5173/
PS> npm run build
✓ built in 974ms
> Using @sveltejs/adapter-static
  Wrote site to "build"
  ✔ done
PS> Get-ChildItem build -Name
_app
about.html
index.html
robots.txt

Enter fullscreen mode Exit fullscreen mode

Open http://localhost:5173, click between the two pages, then stop the server with Ctrl+C before you build. Build output is trimmed.

The build folder is the whole site: one HTML file per page, plus the JavaScript in _app that makes links switch pages without a reload.

Step 5: Push it to GitHub

$ git init -b main
$ git add -A
$ git commit -m "SvelteKit site with adapter-static"
# Create an empty repository named tutorial-sveltekit-app on github.com/new first.
$ git remote add origin https://github.com/YOUR-USERNAME/tutorial-sveltekit-app.git
$ git push -u origin main
To https://github.com/YOUR-USERNAME/tutorial-sveltekit-app.git
 * [new branch] main -> main
branch 'main' set up to track 'origin/main'.


PS> git init -b main
PS> git add -A
PS> git commit -m "SvelteKit site with adapter-static"
# Create an empty repository named tutorial-sveltekit-app on github.com/new first.
PS> git remote add origin https://github.com/YOUR-USERNAME/tutorial-sveltekit-app.git
PS> git push -u origin main
To https://github.com/YOUR-USERNAME/tutorial-sveltekit-app.git
 * [new branch] main -> main
branch 'main' set up to track 'origin/main'.

Enter fullscreen mode Exit fullscreen mode

The project's .gitignore already keeps node_modules, .svelte-kit and build out of Git.

Step 6: Deploy it

  1. In the Light Cloud console, click New... , then Deploy from GitHub.
  2. Search for tutorial-sveltekit-app and click it.

Light Cloud shows SvelteKit and, in the reasons, adapter-static , with Served from the edge. It found adapter-static in package.json, so it deploys the site as static files rather than as a server.

The detection banner SvelteKit, based on package.json, package-lock.json, SvelteKit detected, adapter-static, with Served from the edge highlighted

  1. Optional: under Advanced - build settings, environment variables, domain, scaling you can see npm ci, npm run build and the output directory build. Nothing to change.

The Advanced section with Install command npm ci, Output directory build and Build command npm run build highlighted

  1. Click Deploy and wait for Deployed (30 seconds in my run). Open the address from the URL card:

The Production overview with the Deployed badge and the URL card highlighted

From now on, every push to main redeploys the site by itself.

Step 7: Open a page directly

Open https://main-tutorial-sveltekit-app-yourworkspace.light-cloud.io/about in a new tab, or refresh the page while on About. Light Cloud serves about.html for /about, so the page comes straight from the prerendered file:

The About page opened directly at /about, with the About link in bold

To see that the page really comes prerendered, right-click it and choose View Page Source : the HTML already contains <h1>About</h1>, before any JavaScript runs.

/about/ with a trailing slash works too. A path with no page, such as /nope, falls back to index.html.

Troubleshooting

Error: Encountered dynamic routes

@sveltejs/adapter-static: all routes must be fully prerenderable, but found the following routes that are dynamic:
  - src/routes/
error during build:
Error: Encountered dynamic routes

Enter fullscreen mode Exit fullscreen mode

Prerendering is off. Add export const prerender = true; to src/routes/+layout.ts (Step 2), build again, commit and push.

Top comments (0)