Get started
Install @genko-me/svelte in SvelteKit and fetch in +page.server.ts to display a list and a single entry.
This guide connects genko.me to a SvelteKit app and renders list and detail pages on the server.
To create a workspace and blog API, follow steps 1–3 of the quickstart.
Prerequisites
- A genko.me account and workspace. You need Owner access to create an API and issue an API key.
- An API with at least one saved, published entry. The delivery API does not return drafts by default.
- A SvelteKit 2 and Svelte 5 project.
@genko-me/sveltelists Svelte 5 as a peer dependency. - Node.js 20.12 or later and a package manager.
- A Node.js adapter such as
@sveltejs/adapter-node.
Replace your-workspace with your workspace ID and blog with your endpoint in the examples below.
Install
npm install @genko-me/svelte @genko-me/sdkThis page covers @genko-me/[email protected].
Prepare the schema and types
Generate types and API definitions from your dashboard schema. Run this at the project root.
npx @genko-me/cli initThe CLI detects output locations as if this were Next.js, so it writes to the project root's lib/ in SvelteKit. Move the files to src/lib/ and update schemaOutput and clientOutput in genko.config.json. Later genko pull commands use that configuration.
{ "schemaOutput": "src/lib/schema.ts", "clientOutput": "src/lib/client.ts"}If you do not use the CLI, write src/lib/schema.ts yourself. Match field IDs and types to the dashboard schema.
import { listApi } from "@genko-me/sdk";
export type Blog = { title: string; body: string };
export const apis = { blog: listApi<Blog>({ fields: [ { id: "title", type: "text" }, { id: "body", type: "richtext" }, ], }),};Set the API key and workspace ID
Issue an API key in the dashboard under Settings and plan → API keys (/w/your-workspace/settings/api-keys). Give it a descriptive name and expiry. If you choose “Selected APIs only,” allow blog.
In the generated src/lib/client.ts, replace process.env.GENKO_API_KEY with SvelteKit's $env/dynamic/private.
import { createGenko } from "@genko-me/sdk";import { env } from "$env/dynamic/private";import { apis } from "./schema";
if (!env.GENKO_API_KEY) throw new Error("Set the GENKO_API_KEY environment variable.");
export const genko = createGenko({ workspace: "your-workspace", apiKey: env.GENKO_API_KEY, apis,});export const client = genko.apis;$env/dynamic/private prevents browser code from importing this file at build time. Importing client in a .svelte file produces this error:
Cannot import $env/dynamic/private into code that runs in the browser,as this could leak sensitive information.Use $env/dynamic/private rather than $env/static/private to read the value at runtime instead of build time. The build can succeed with a key set only at the host, and rotating the key needs no rebuild.
Pass GENKO_API_KEY as an environment variable to the server process. Pagination signing reads process.env.GENKO_API_KEY directly, so writing only to .env is insufficient. In development, load it with --env-file.
{ "scripts": { "dev": "node --env-file=.env ./node_modules/vite/bin/vite.js dev" }}GENKO_API_KEY=your-issued-keyMake sure Git does not track .env. Set the same variable in the hosting environment for production. Without pagination, .env alone works, but passing the variable from the start avoids surprises later.
Show a list
Fetch in +page.server.ts. Pass the return value of fetchGenkoList directly as props to ListClient.
import { fetchGenkoList } from "@genko-me/svelte/server";import { client } from "$lib/client";import type { PageServerLoad } from "./$types";
export const load: PageServerLoad = async () => ({ list: await fetchGenkoList({ api: client.blog, fields: ["title", "publishedAt"], href: "/blog/{id}", limit: 10, orders: "-publishedAt", }),});List the IDs displayed by ListItem in fields. Unlike React, Svelte cannot collect them from JSX, so specify them here. Omitting fields fetches everything. An ID missing from the schema causes a configuration error. {id} in href is included automatically.
<script lang="ts"> import { ListClient, ListItem, Error, Null } from "@genko-me/svelte";
let { data } = $props();</script>
<h1>Blog</h1>
{#if data.list.status === "ok"} <ListClient {...data.list.data} href={'/blog/{id}'}> {#snippet children()} <article> <h2><ListItem id="title" /></h2> <time><ListItem id="publishedAt" format="yyyy/MM/dd" timeZone="Asia/Tokyo" /></time> </article> {/snippet} {#snippet empty()}<Null>No published entries yet.</Null>{/snippet} </ListClient>{:else} <Error template />{/if}The children snippet defines one item's layout. Pass empty, error, loading, and pagination as separate snippets; they are not repeated. If you omit a state snippet, nothing appears for that state.
Because {id} in an href template resembles a Svelte expression, pass it as a string: href={'/blog/{id}'}. The default time zone is UTC.
Start the development server and open /blog. Seeing entries confirms the connection.
Show a single entry
import { error } from "@sveltejs/kit";import { fetchGenkoEntry } from "@genko-me/svelte/server";import { client } from "$lib/client";import type { PageServerLoad } from "./$types";
export const load: PageServerLoad = async ({ params }) => { const entry = await fetchGenkoEntry({ api: client.blog, id: params.id, fields: ["title", "body"], }); if (entry.status === "empty") error(404, "Entry not found."); return { entry };};<script lang="ts"> import { EntryProvider, ViewItem, Error } from "@genko-me/svelte";
let { data } = $props();</script>
{#if data.entry.status === "ok"} <EntryProvider {...data.entry.data}> {#snippet children()} <article> <h1><ViewItem id="title" /></h1> <ViewItem id="body" /> </article> {/snippet} </EntryProvider>{:else} <Error template />{/if}fetchGenkoEntry returns three states: ok for an entry, empty for a missing entry (404 or invalid draft key), and error for a fetch or configuration failure. The example converts empty into SvelteKit's error(404). You can instead show it inside a Null snippet.
Omit id for object APIs.
Set body heading and paragraph spacing and image maximum width in your site's CSS. ViewItem adds no default styling.
article :global(h2) { margin-block: 2em 0.75em; font-size: 1.5em; }article :global(p) { margin-block: 1em; }article :global(img) { max-width: 100%; height: auto; }Troubleshooting
| Symptom | Check |
|---|---|
| Empty list | Is an entry saved and Published? Has its publication end time passed? |
status is error | Look for a [genko.me] line in the server log. It includes configuration errors or HTTP status codes. |
401 | Is the key valid, and can the server process read GENKO_API_KEY? |
403 | Does the key belong to the workspace you are connecting to? |
404 | Are the workspace ID and blog endpoint correct, and does the key allow blog? |
createHmac is not exported by "__vite-browser-external" | You imported @genko-me/svelte/server from a .svelte file. Move the import to a server file. |
Next, use the tutorial to add pagination and draft previews.
Last updated: October 16, 2026