4 min read
On this page

Prerendering

Prerendering generates HTML at build time instead of on each request. When you deploy a prerendered SvelteKit app, your pages are static HTML files sitting on a CDN. No server runs, no load function executes per request, and the page loads as fast as a static file can be served. SvelteKit's prerender crawler automatically discovers pages and renders them during vite build.

Enabling Prerendering

Export prerender = true from +page.ts or +page.server.ts:

// src/routes/about/+page.ts
export const prerender = true;

That single line tells SvelteKit to render this page at build time. The HTML is written to disk and served statically. You can also enable it at the layout level to prerender an entire section:

// src/routes/blog/+layout.ts
export const prerender = true;

Every page under /blog will now be prerendered, including dynamic routes like /blog/my-first-post.

When Prerendering Works

Prerendering is ideal when the content is the same for every visitor and changes only when you redeploy. Common cases include:

// Blog posts — content changes on deploy, same for everyone
// src/routes/blog/[slug]/+page.server.ts
import type { PageServerLoad } from './$types';

export const prerender = true;

export const load: PageServerLoad = async ({ params }) => {
  const post = await getPostBySlug(params.slug);

  return {
    title: post.title,
    content: post.content,
    publishedAt: post.publishedAt
  };
};
// Documentation pages — static content, structured navigation
// src/routes/docs/[...path]/+page.server.ts
import type { PageServerLoad } from './$types';

export const prerender = true;

export const load: PageServerLoad = async ({ params }) => {
  const doc = await loadMarkdown(`docs/${params.path}.md`);
  return { doc };
};
// Marketing pages — landing pages, pricing, feature lists
// src/routes/pricing/+page.ts
export const prerender = true;

The pattern is consistent: if the page does not depend on who is viewing it, prerender it.

When Prerendering Does Not Work

Prerendering fails when the content depends on the request. At build time, there is no authenticated user, no cookies, no query parameters from a real visitor.

// This will NOT work with prerendering
// src/routes/settings/+page.server.ts
import type { PageServerLoad } from './$types';

export const load: PageServerLoad = async ({ cookies, locals }) => {
  // No cookies at build time
  const session = cookies.get('session');
  // No authenticated user at build time
  const user = locals.user;

  return { user };
};

Pages that cannot be prerendered include user dashboards and profile pages, real-time data displays like stock tickers or live feeds, pages with content based on search parameters, and anything that reads cookies or request headers in the load function.

The Prerender Crawler

SvelteKit does not require you to list every URL to prerender. During vite build, the crawler starts at your entry points and follows links to discover all prerenderable pages:

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

/** @type {import('@sveltejs/kit').Config} */
const config = {
  kit: {
    adapter: adapter(),
    prerender: {
      // Starting points for the crawler
      entries: ['/', '/blog', '/docs'],

      // Handle HTTP errors during prerendering
      handleHttpError: ({ path, referrer, message }) => {
        // Ignore missing og:image links
        if (path.startsWith('/og/')) return;

        // Throw on everything else
        throw new Error(message);
      }
    }
  }
};

export default config;

The crawler finds links in your rendered HTML. If page A links to page B and both have prerender = true, the crawler renders both. This means your navigation and internal links drive discovery automatically.

Prerendering Dynamic Routes

Dynamic routes like [slug] or [id] need the crawler to know which parameter values exist. You provide them through the entries function or by linking to them from other prerendered pages:

// src/routes/blog/[slug]/+page.server.ts
import type { EntryGenerator, PageServerLoad } from './$types';

export const prerender = true;

// Tell the crawler which slugs exist
export const entries: EntryGenerator = async () => {
  const posts = await getAllPosts();
  return posts.map((post) => ({
    slug: post.slug
  }));
};

export const load: PageServerLoad = async ({ params }) => {
  const post = await getPostBySlug(params.slug);
  return { post };
};

The entries function returns an array of parameter objects. SvelteKit renders one page per entry. This is how you prerender hundreds of blog posts or documentation pages.

// src/routes/docs/[category]/[page]/+page.server.ts
import type { EntryGenerator } from './$types';

export const prerender = true;

export const entries: EntryGenerator = async () => {
  const docs = await getAllDocs();
  return docs.map((doc) => ({
    category: doc.category,
    page: doc.slug
  }));
};

Prerendering API Routes

Server routes (+server.ts) can also be prerendered. This is useful for generating JSON files, RSS feeds, or sitemaps at build time:

// src/routes/blog/rss.xml/+server.ts
import type { RequestHandler } from './$types';

export const prerender = true;

export const GET: RequestHandler = async () => {
  const posts = await getAllPosts();
  const xml = generateRssFeed(posts);

  return new Response(xml, {
    headers: {
      'Content-Type': 'application/xml'
    }
  });
};
// src/routes/sitemap.xml/+server.ts
import type { RequestHandler } from './$types';

export const prerender = true;

export const GET: RequestHandler = async () => {
  const pages = await getAllPrerenderedPaths();
  const sitemap = generateSitemap(pages);

  return new Response(sitemap, {
    headers: {
      'Content-Type': 'application/xml'
    }
  });
};

Full Static Site with adapter-static

When every page in your app is prerendered, you can use adapter-static to output a fully static site:

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

/** @type {import('@sveltejs/kit').Config} */
const config = {
  kit: {
    adapter: adapter({
      pages: 'build',
      assets: 'build',
      fallback: undefined,
      precompress: true
    })
  }
};

export default config;
// src/routes/+layout.ts
// Prerender everything
export const prerender = true;

The output is a build/ directory with HTML, CSS, JS, and assets. Deploy it to any static host: Cloudflare Pages, GitHub Pages, Netlify, or an S3 bucket behind CloudFront.

If you want a static site with a SPA fallback for dynamic routes, set the fallback option:

adapter({
  fallback: '200.html' // or 'index.html' depending on host
})

Prerender Configuration Options

Fine-tune prerendering behavior in svelte.config.js:

const config = {
  kit: {
    prerender: {
      // Max concurrent prerender operations
      concurrency: 4,

      // Crawl links in rendered pages
      crawl: true,

      // Entry points for the crawler
      entries: ['*'],

      handleHttpError: 'warn', // 'fail' | 'warn' | 'ignore' | custom fn

      handleMissingId: 'warn',

      handleEntryGeneratorMismatch: 'warn'
    }
  }
};

Setting entries: ['*'] starts from every non-dynamic route. The crawler then follows links to discover dynamic routes. Use explicit entries when you need more control.

Common Pitfalls

  • Forgetting to provide entries for dynamic routes. If you prerender [slug] without an entries function and no other prerendered page links to it, SvelteKit cannot discover the route. You will get zero pages for that route.
  • Using request-dependent data in prerendered pages. Cookies, headers, and URL search parameters are not available at build time. A prerendered page sees an empty request context.
  • Not handling external API failures during build. If your load function calls an external API and it is down during build, the prerender fails. Add error handling or use handleHttpError to gracefully handle failures.
  • Prerendering pages with forms. Form actions require a server to process submissions. A prerendered page can display a form, but the action needs a server endpoint or must use method="GET" with query parameters.
  • Assuming prerendered pages update automatically. Prerendered content is frozen at build time. To update it, you must rebuild and redeploy. For content that changes frequently, consider SSR with caching instead.

Key Takeaways

  • Prerendering generates static HTML at build time with export const prerender = true.
  • Use it for content that is the same for every visitor: blogs, docs, marketing pages, landing pages.
  • The prerender crawler discovers pages by following links from entry points. You do not need to list every URL manually.
  • Dynamic routes need an entries function to tell the crawler which parameter values exist.
  • API routes can be prerendered too, useful for RSS feeds, sitemaps, and JSON data files.
  • adapter-static produces a fully static site when all routes are prerendered.
  • Prerendered pages cannot access cookies, headers, or user-specific data. Use SSR for those cases.