By day 30 of building with SvelteKit, you've likely shipped dynamic routes, database queries, and real-time features. Now comes a different challenge: how do you serve truly fast pages when most of your users hit the same URLs? Prerendering static sites is the answer, and it's one of the most powerful optimizations SvelteKit offers. We'll walk through the practical setup, show you where prerendering shines, and explain the trade-offs you need to consider.
Why prerendering matters in production
Prerendering means building your pages at deploy time, not request time. Instead of running server-side code when a user visits /blog/my-post, SvelteKit generates that HTML file during the build and your CDN serves it directly. No database query latency. No server CPU. No cold starts. Just static HTML and CSS shipped from an edge location closest to your user.
Compare this to server-side rendering (SSR) on every request: a user in Tokyo hits your European server, waits for database queries, waits for template rendering, then finally gets the page. With prerendering, that same user pulls cached HTML from a Tokyo CDN node in milliseconds.
We've seen this pattern across e-commerce product catalogs, SaaS documentation sites, and travel company blog networks. When your content doesn't change multiple times per second, prerendering eliminates an entire layer of infrastructure cost and latency.
Setting prerender = true in layouts
The simplest way to enable prerendering in SvelteKit is to add prerender = true to your layout or page file. This tells SvelteKit: "Build this route as static HTML during the build step."
// src/routes/+layout.server.ts
export const prerender = true;Add this to your root layout, and SvelteKit will prerender every page and layout in your entire app. For a documentation site with 200 pages, this means 200 static HTML files generated at deploy time, ready to serve immediately.
You can also set prerender = true on individual page files if you want selective prerendering:
// src/routes/blog/[slug]/+page.server.ts
export const prerender = true;
export async function load({ params }) {
const post = await db.posts.findOne({ slug: params.slug });
return { post };
}When SvelteKit sees prerender = true, it knows to execute your load function during build, capture the returned data, and bake it into static HTML. The user never runs that database query at runtime.
How the crawler follows links automatically
Here's where SvelteKit gets smart: by default, SvelteKit crawls your app like a search engine. It starts at the root, finds all <a> tags, follows them, and prerender everything it discovers. This means you rarely need to manually list which routes to prerender.
Say you have a blog homepage that links to 50 posts. When you run npm run build with prerendering enabled, SvelteKit:
- Builds your homepage.
- Finds all
<a href="/blog/...">links. - Follows each link and builds those pages too.
- Repeats until no new links are found.
This crawler respects your site structure naturally. If a page isn't linked anywhere, it won't be prerendered unless you explicitly add it to the entries config (more on that below).
The tradeoff: if your site is truly massive—thousands of products or user-generated content pages—the crawler can take a long time or miss pages that aren't discoverable via links. This is where the next section comes in.
Using entries for dynamic routes
Not all routes can be discovered by crawling. If you have dynamic segments like /product/[id] where IDs come from a database, the crawler can't know which IDs exist without querying the database.
That's what entries is for. You provide a list of which pages should be prerendered:
// src/routes/product/[id]/+page.server.ts
export const prerender = true;
export async function entries() {
const products = await db.products.findAll();
return products.map(p => ({
id: p.id
}));
}
export async function load({ params }) {
const product = await db.products.findOne({ id: params.id });
return { product };
}The entries() function runs during build and returns an array of parameter objects. SvelteKit then calls load() with each set of parameters, prerendering every combination. A store with 5,000 products becomes 5,000 static HTML files.
This works beautifully for e-commerce: run your build at 2am, pull the current product list, prerender every product page, deploy the static files to your CDN. Users see fresh inventory pages with zero latency.
For routes with multiple dynamic segments, return all combinations:
// src/routes/blog/[year]/[month]/[slug]/+page.server.ts
export const prerender = true;
export async function entries() {
const posts = await db.posts.findAll();
return posts.map(p => ({
year: p.date.getFullYear().toString(),
month: String(p.date.getMonth() + 1).padStart(2, '0'),
slug: p.slug
}));
}Great for blogs and docs
Blogs and documentation are the canonical use cases for prerendering. Your content rarely changes mid-request, yet your users expect instant load times. Prerendering is perfect.
Blog example: You publish a new post, trigger a rebuild, and within seconds the static HTML is on your CDN. Readers hit the page with zero server overhead. Comments might come from a separate API (Disqus, custom backend), so the prerendered HTML doesn't need to include dynamic comments—just a placeholder.
Docs example: A framework's documentation site with hundreds of pages. Each page is stable. Prerender all of them. Users get instant page loads anywhere in the world. Search engines see complete, pre-rendered HTML for better crawling.
Both scenarios share a key property: content is curated by humans or periodic jobs, not requested per-user. This is the sweet spot for prerendering.
If your site requires user personalization—a dashboard showing your own data, recommendations based on browsing history, or real-time chat—prerendering won't work for those routes. Use SSR or client-side rendering instead. But for public-facing content, prerendering scales endlessly.
Common mistakes
Forgetting to set prerender = true on all routes. If you want prerendering, set it at the root layout or explicitly on each route. Partial prerendering is valid but can be confusing if you don't document it clearly.
Prerendering routes that depend on request headers or cookies. Prerendered pages are static—they can't read the user's request headers, IP address, or cookies. If your page requires authentication to render, don't prerender it. Use export const prerender = false to force SSR instead.
Not updating entries() when your data changes. If you add 100 new products to your store but don't redeploy, the prerendered site won't include them. Set up automatic rebuilds (via webhooks) when your content updates, or accept that prerendered sites are "eventually consistent."
Prerendering everything, including admin pages. Some routes shouldn't be prerendered. An /admin/dashboard or /account/settings page must be dynamic. Use export const prerender = false on sensitive routes.
Ignoring build time. Prerendering 10,000 pages takes time. If your build already takes 10 minutes and you add 10,000 page prerendering, it's now 20 minutes. This affects your deployment pipeline. Monitor build times and consider splitting large prerender jobs or using incremental static regeneration if your CI/CD supports it.
Key takeaways
- Prerendering generates static HTML at build time, served instantly from a CDN with no server overhead.
- Set
export const prerender = truein your layout or page to enable it for that route. - SvelteKit's crawler automatically discovers and prerender linked pages—no manual configuration needed for most content.
- Use
entries()to provide dynamic parameters that can't be discovered by crawling, like product IDs from a database. - Prerendering is ideal for blogs, documentation, and other human-curated or job-scheduled content that doesn't change per-request.
- Avoid prerendering routes that depend on user headers, cookies, authentication, or personalization.
- Set up automatic rebuilds when your prerendered content changes, or accept eventual consistency.
- Monitor build times as your prerender scope grows; 10,000 pages will noticeably slow your deployment pipeline.






Comments
Loading comments…