What is your developer-dependent CMS actually costing you? Calculate your costs and get a full report
What is your developer-dependent CMS actually costing you? Calculate your costs and get a full report
How it works
How the SvelteKit starter routes every URL through a single catch-all route driven by the Agility sitemap, and prerenders each page with entries().
Legacy — SvelteKit docs are no longer actively maintained. The SvelteKit starter targets SvelteKit 2; SvelteKit 3 shipped in October 2026. See why we recommend Next.js and .NET. If you need SvelteKit, let us know.
In the SvelteKit starter, the Agility sitemap is the router. Editors create pages (and their URLs) in Agility, and a single catch-all route renders whatever the sitemap says exists — no new route files when a page is added.
All CMS-driven pages flow through one route:
src/routes/
├─ +layout.server.ts # resolves the Agility page for the current path
├─ +layout.svelte # site chrome (header, footer)
└─ [...path]/
├─ +page.server.ts # prerender config + entries()
└─ +page.svelte # picks the template and renders it
[...path] is a SvelteKit rest parameter — it matches /, /about-us, /blog/my-post, anything. The actual page lookup happens in the layout load, so the header and page resolve together:
// src/routes/+layout.server.ts
export const load: LayoutServerLoad = async ({ params, cookies }) => {
const isPreviewMode = cookies.get('previewMode') === 'true';
const isPreview = NODE_ENV === 'development' && isPreviewMode;
const path = params.path || 'home'; // "/" falls back to the home page
const { page } = await getAgilityPage({ path, isPreview });
page.header = await getSiteHeader(isPreview);
return { isPreview, page };
};
getAgilityPage asks the Content Fetch API for the page by its sitemap path — one call returns the sitemap node, the page, its zones, and (thanks to contentLinkDepth: 4) the content each component needs:
// src/lib/agility/getAgilityPage.ts
const api = agility.getApi({
guid: AGILITY_GUID,
apiKey: isPreview ? AGILITY_API_PREVIEW_KEY : AGILITY_API_FETCH_KEY,
isPreview,
});
const page = await api.getPageByPath({
pagePath: `/${path}`,
locale: AGILITY_LOCALES,
channelName: AGILITY_SITEMAP,
contentLinkDepth: 4,
});
if (!page) error(404, 'Page not found.');
If the path isn't in the sitemap, the route 404s — editors deleting a page removes it from the site automatically.
The starter prerenders the whole site at build time. entries() tells SvelteKit which paths exist by walking the flat sitemap:
// src/routes/[...path]/+page.server.ts
export async function entries() {
const sitemap = await getSitemapFlat({ isPreview: false });
return Object.keys(sitemap)
.filter((key) => key !== '/home') // "/" already covers home
.map((key) => ({ path: key.slice(1) }));
}
export const prerender = true;
export const ssr = true;
Because pages are prerendered, a content publish requires a rebuild to show up in production. Wire an Agility webhook to your host's build hook — see the deployment guides for Vercel, Netlify, Azure, and AWS. (You can also switch the route to pure SSR by removing
prerenderand caching responses — the Astro starter docs show that pattern.)
Back in the page component, the resolved page's templateName decides which Svelte layout template renders it, and <svelte:head> gets the SEO fields:
<!-- src/routes/[...path]/+page.svelte -->
<svelte:head>
<title>{`${sitemapNode.title} | ${title}`}</title>
<meta name="description" content={seo.metaDescription} />
</svelte:head>
{#if templateName === "Main Template"}
<MainTemplate {...data} />
{:else}
Template not found.
{/if}
See Pages for what happens inside the template, and Components for how each zone renders its components.