Quickstart

Three steps. No cliff.

Everything below is what actually happens: the transcript is real output from the published package, not marketing copy. Init leaves a running app, or tells you precisely why not.

01

Install

In any SvelteKit project (Svelte 5). Tailwind v4 is the one hard prerequisite; the admin is built on it, and init will tell you if it’s missing rather than letting the site 500.

install
npm i highseam
npm i -D tailwindcss @tailwindcss/vite
02

Scaffold

npx highseam-init copies thin route stubs that import from the package: REST, GraphQL, admin, media. They’re yours: delete what you don’t need, customize what you do. It wires your vite.config.ts and root layout for you.

npx highseam-init
$ npx highseam-init

highseam scaffolded.

  created:
    + src/routes/admin/+layout.server.ts
    + src/routes/admin/+page.svelte
    + src/routes/admin/[collection]/+page.svelte
    + src/routes/admin/[collection]/[id]/+page.svelte
    + src/routes/admin/create-first-user/+page.svelte
    + src/routes/admin/login/+page.svelte
      … 11 more admin stubs
    + src/routes/api/[collection]/+server.ts
    + src/routes/api/[collection]/[id]/+server.ts
    + src/routes/api/[collection]/[id]/publish/+server.ts
    + src/routes/api/[collection]/import/+server.ts
    + src/routes/api/graphql/+server.ts
      … 9 more API stubs
    + src/routes/media/[...key]/+server.ts
    + src/cms.config.ts
    + src/hooks.server.ts
    + src/highseam.css
    + src/app.d.ts
  wired:
    ~ vite.config.ts (added @tailwindcss/vite plugin)
    ~ src/routes/+layout.svelte (imported highseam.css)

Next steps:
  1. REQUIRED — install Tailwind v4 (the admin needs it):
       npm i -D tailwindcss @tailwindcss/vite
  2. npm run dev → open /admin and create the first user (they become admin).
  3. Shape your content in src/cms.config.ts — collections there get a database,
     REST + GraphQL APIs, and admin UI automatically. Your own routes are untouched;
     the CMS lives under /admin and /api.

Production (Cloudflare): bind D1 as DB, R2 as BUCKET, set CMS_SECRET.
03

Run

Open /admin and it prompts you to create the first user; they become an admin. A pristine dashboard walks you through first steps.

dev
npm run dev   # → http://localhost:5173/admin

The starter config is three collections that show the flagship features, not a bare stub: auth users, a media library with the metadata conventions pre-declared, and pages with drafts, a tabbed form, and rich text carrying image embeds and a typed component:

src/cms.config.ts (starter, excerpt)
// src/cms.config.ts — the starter ships three collections:
// users (auth), media (uploads + metadata conventions), pages
// (drafts, tabs, rich text with image embeds and a typed Callout).

const pages = defineCollection({
  slug: 'pages',
  versions: { drafts: true },
  admin: { useAsTitle: 'title', groupsAs: 'tabs' },
  access: {
    read: ({ req }) =>
      req.user ? true : { _status: { equals: 'published' } }
  },
  fields: [
    { name: 'title', type: 'text', required: true },
    { name: 'slug', type: 'text', required: true, unique: true },
    { name: 'coverImage', type: 'upload', relationTo: 'media' },
    { name: 'content', type: 'richText', uploads: 'media' }
  ]
});

Production is two commands away

The same engine that ran on a local JSON file now runs on Workers against D1 and R2; the CMS auto-detects the bindings. This very page is the proof: you’re reading it out of D1, rendered on a Worker.

deploy to Cloudflare Pages
wrangler d1 create highseam-db   # bind as DB in wrangler.toml
# uncomment the R2 bucket binding for uploads
# set CMS_SECRET as a Pages env var
npm run deploy

Gotchas

  • Table-backed collections in local dev: your local D1 starts empty, so a collection mapped to a remote table reports “table does not exist” until you seed it with npx wrangler d1 execute <db> --local --file schema.sql.
  • pnpm monorepos: don’t nest the CMS app inside another pnpm project; hoisting breaks the package’s dependency resolution. Scaffold a sibling directory.
  • Don’t name your app highseam: npm refuses a dependency whose name matches the host package.

The full reference (fields, access control, hooks, localization, the lot) lives in the README on GitHub.

highseam ·MIT·built on SvelteKit + Cloudflare ·this site runs on highseam