Noma
SvelteKit · SSR & SSG · 2026

CMS for Svelte & SvelteKit

SvelteKit’s server load functions and server-only modules are a natural fit for Noma: fetch published entries with the JavaScript SDK on the server, expose only serializable data to components, and never ship personal API keys to the browser.

Architecture

How SvelteKit and Noma align

SvelteKit distinguishes universal vs server load. CMS credentials belong exclusively to the server graph. The server-only module rules prevent accidental imports of $env/static/private into client bundles—use $lib/server/… for your Noma client factory.

There is no official Svelte guide in Documentation yet; this page is the SEO-focused integration reference. Patterns match our Next.js and Astro guides: same SDK, same API surface.

Setup

Environment variables

Add secrets to .env (not prefixed with PUBLIC_):

NOMA_PROJECT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
NOMA_API_KEY=noma_...

Reference them with $env/static/private or $env/dynamic/private inside server-only modules. For end-user auth that runs in the browser, you may expose PUBLIC_NOMA_PROJECT_ID only—never the API key.

Install

SDK factory on the server

npm install @nomacms/js-sdk
// src/lib/server/noma.ts
import { createClient } from "@nomacms/js-sdk";
import { NOMA_API_KEY, NOMA_PROJECT_ID } from "$env/static/private";
 
export function getNomaServerClient() {
  if (!NOMA_PROJECT_ID || !NOMA_API_KEY) {
    throw new Error("Missing Noma credentials");
  }
  return createClient({ projectId: NOMA_PROJECT_ID, apiKey: NOMA_API_KEY });
}
Listing

Server load for an index page

// src/routes/posts/+page.server.ts
import { getNomaServerClient } from "$lib/server/noma";
import type { PageServerLoad } from "./$types";
 
export const load: PageServerLoad = async () => {
  const noma = getNomaServerClient();
  const result = await noma.content.list("posts", {
    state: "published",
    paginate: 24,
    sort: "created_at:desc",
  });
  const posts = "data" in result ? result.data : result;
  return { posts };
};
<!-- src/routes/posts/+page.svelte -->
<script lang="ts">
  import type { PageData } from "./$types";
  let { data }: { data: PageData } = $props();
</script>
 
<ul>
  {#each data.posts as post}
    <li><a href={"/posts/" + post.uuid}>{String(post.fields?.title ?? post.uuid)}</a></li>
  {/each}
</ul>

Returned data must be serializable. Entry objects from Noma are plain JSON-compatible structures; custom fields live under fields.

Detail routes

Dynamic [id] pages and 404s

// src/routes/posts/[id]/+page.server.ts
import { error } from "@sveltejs/kit";
import { NotFoundError } from "@nomacms/js-sdk";
import { getNomaServerClient } from "$lib/server/noma";
import type { PageServerLoad } from "./$types";
 
export const load: PageServerLoad = async ({ params }) => {
  const noma = getNomaServerClient();
  try {
    const post = await noma.content.get("posts", params.id, { state: "published" });
    return { post };
  } catch (e) {
    if (e instanceof NotFoundError) error(404, "Not found");
    throw e;
  }
};
Prerendering

SSG at build time

Enable prerender for routes that can be built ahead of time. Your load function runs at build time when the page is prerendered—call Noma then the same way you would on each request. For large collections, batch UUIDs or use pagination in a build script that feeds entries() if you need fine-grained control.

// src/routes/posts/[id]/+page.ts
export const prerender = true;
API routes

+server.ts and webhooks

Use +server.ts to verify Noma webhooks, proxy uploads, or expose JSON. Keep verification logic and secrets server-side.

// src/routes/api/cms-hook/+server.ts
import { json, error } from "@sveltejs/kit";
import type { RequestHandler } from "./$types";
 
export const POST: RequestHandler = async ({ request }) => {
  const secret = request.headers.get("x-webhook-secret");
  if (secret !== process.env.CMS_WEBHOOK_SECRET) error(401, "Unauthorized");
  const payload = await request.json().catch(() => ({}));
  // Verify Noma signature if configured, then trigger redeploy or custom cache busting.
  console.info("CMS event", payload);
  return json({ received: true });
};

Adjust invalidation to your SvelteKit version and hosting model—some teams call host APIs (Netlify, Vercel) from webhooks instead of in-process cache APIs.

Project Auth

End users vs CMS keys

Personal API keys are for your integration layer. If visitors sign in through Noma Project Auth, use HttpOnly cookies and server routes for token exchange—mirror the patterns described in the SDK README for Next.js, adapted to SvelteKit +server.ts.

AI tooling

MCP and skills

Use @nomacms/mcp-server in your editor. The open-source skills repo focuses on Next.js, Nuxt, and Astro today; for SvelteKit, point agents at this page and the SDK setup reference.

Repository: Noma agent skills on GitHub.

Reference

Further reading

Now available

Start building with Noma

Create a free account, spin up a project, and ship structured content with our API, SDK, and AI tools.