SvelteKit and Headless CMS: Directus, Strapi, Sanity, or Payload

Svelte, SvelteKit, and Headless CMS: Choosing and Connecting Directus, Strapi, Sanity, or Payload

A headless CMS separates the editing interface from the frontend that presents content. SvelteKit can fetch that content and render it into routes and pages. The appeal is reuse: one editorial source can serve a website, an app, a newsletter, and other channels. Choosing a CMS does not automatically solve content modelling, permissions, preview, or caching, though. Start by deciding who edits which content, in what state, and which version visitors are allowed to see.

A sound SvelteKit baseline

SvelteKit’s load function is the standard path for supplying page data to a component. For public article lists and detail pages where SEO matters, a server load that fetches only the fields required to render is often practical. Keep calls that need a secret token, draft preview, or privileged content on the server. Reserve browser fetches for interactions that are genuinely safe to expose after a user signs in.

// src/routes/posts/[slug]/+page.server.ts
export const load = async ({ params, fetch, setHeaders }) => {
  const res = await fetch(`${CMS_URL}/posts?slug=${encodeURIComponent(params.slug)}`);
  if (!res.ok) throw error(res.status, 'Post not found');
  setHeaders({ 'cache-control': 'public, max-age=60' });
  return { post: await res.json() };
};

That is a conceptual example, not a copy-paste integration. API shape, permissions, and cache headers must match the chosen CMS and hosting platform. Do not inject an external response into HTML without a maintained, validated renderer for its content format—Portable Text, rich text, or Markdown. Treat a slug as a stable public identifier and manage changes, redirects, and duplicates in the editorial workflow.

Where the four systems differ

Directus is well suited to an approach centered on a new or existing database, with data management, permissions, and APIs layered around it. It can be a natural fit when teams operate relational models and want REST or GraphQL access. Strapi automatically creates Content API endpoints when a content type is created. Its Strapi 5 REST API models documents, locales, and draft/published status; relations and media are not automatically populated in a default response, so request the population you truly need.

Sanity stores structured JSON documents in its Content Lake and uses GROQ as its native query language to shape a response. A GraphQL API can also be generated and deployed from the schema, but Sanity recommends evaluating GROQ first. Payload suits teams that want a CMS close to application code. It offers REST, Local, and GraphQL APIs; verify the project’s configured base routes, authorization, and locale behavior rather than assuming defaults. None is universally “faster.” The model, content volume, image delivery, caching, and deployment locality decide observed latency.

Design the operational pieces first

  1. Content contract: declare title, slug, body blocks, SEO fields, author, publication status, and locales in both CMS schema and TypeScript types.
  2. Public/private boundary: give the public API only required read access; keep editing, preview, and webhook secrets on the server with validation.
  3. Preview: connect the CMS’s draft identity to a protected SvelteKit preview path without mixing draft responses into general caches.
  4. Cache invalidation: validate a publish/update webhook, then revalidate only related paths or purge the right CDN key. Webhooks are public endpoints, so verify signatures and account for retries.
  5. Failure experience: distinguish a genuine 404 from a CMS 500 or timeout, and decide whether the last verified public content may be served.

A selection rule that holds up

Consider Directus for database-centric data operations; Strapi for its Node-oriented content-type and plugin workflow; Sanity when editorial experience and flexible content-shaped queries are central; and Payload when deeply code-controlled CMS behavior is desirable. Do not select by name alone. Build a small proof using one representative content model, list/detail/draft-preview flow, two permission roles, and one webhook. Have both editors and developers test it. That reveals the important constraints before content and URL history make migration costly.

Primary source

https://svelte.dev/docs/kit/load
https://docs.directus.io/guides/connect/
https://docs.strapi.io/cms/api/rest

koen