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.
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.
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.
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 });
}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.
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;
}
};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;+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.
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.
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.