Comparison · for developers

Primo vs Payload.

Payload is a serious piece of engineering: a code-first, TypeScript-native headless CMS that’s really an application framework. You declare collections in config, it generates an admin UI, and your app fetches content over REST/GraphQL. Developers love that the schema lives in code — and they should. But for a content site, the question is what sits between your content and the page your client sees. With Payload, it’s an API and a separate admin dashboard. With Primo, the editor and the renderer read the same files, and the client edits in place on the live page.

The short version
Pick Payload if…

…you’re building an actual web app or a backend, you need a content API feeding mobile or multiple front-ends, or you want mature auth, access control, and hooks over deeply structured data. Payload is framework-agnostic via its API and excellent at all of this — it’s the right tool when content is a service, not a site.

Pick Primo if…

…you’re building a content site and want the editor and renderer to read the exact same files — no API to fetch through, no separate admin to keep in sync. Your agent edits the block as code; your client edits that same block in place on the rendered page. The trade is that Primo is Svelte today.

Where they actually differ

Payload
Primo
Schema definition
Code-first, in TypeScript config
A fields.yaml beside each block
Where the client edits
~ A separate generated admin dashboard
In place, on the rendered page
Layer between content & page
REST/GraphQL API your app fetches
None — editor & renderer read files
Framework
Framework-agnostic via the API
~ Svelte today; SvelteKit integrate coming
Best suited for
~ Apps, backends, multi-channel content
Content sites — marketing, portfolio, local
Self-host
Yes, self-hostable
Yes (SQLite via PocketBase)
License
Open source (MIT)
Open source (MIT)
Where Payload genuinely wins

Code-first schema, and a real backend behind it.

Let's be fair before we argue. Payload puts your schema in code — collections defined in TypeScript config, fully typed end to end — and that's exactly what a lot of developers want. It generates a clean admin UI from that config, then serves content over REST and GraphQL so any front-end, in any framework, can fetch it. That API-in-the-middle is a feature: it's what lets the same content feed a website, a mobile app, and a dozen other surfaces at once.

It's also far more than a CMS. Payload has mature access control, authentication, and hooks, so it doubles as an application framework — you can build a genuine custom backend on top of it. For structured content and apps, that depth is hard to beat, and Primo isn't trying to.

The one architectural difference

Payload’s editor is a separate admin. Primo’s editor is the page.

Here's the mechanism, stated fairly. In Payload, you define collections in code; Payload generates an admin dashboard from that schema, and your front-end fetches content from Payload's API to render it. So there are two decoupled things: the admin where editing happens, and the rendered site that queries the API. They're connected by the API, not the same surface.

Primo collapses that. A block is a Svelte component plus a fields.yaml next to it. The editor generates its UI from that field schema, and the renderer reads the same files — there's no API to fetch through and no second app to keep aligned. That's the verified claim worth weighing: the block your agent edits as code and the page your client edits are the same files, and the editor is generated from the block's field schema, with no glue code.

What it means day to day

Where your client actually works.

With Payload, your client logs into the admin dashboard, edits fields in forms, saves, and then looks at the rendered site to confirm it landed the way they meant. The admin is decoupled from the front-end, so editing and seeing the result are two steps in two places.

With Primo, your client opens the live page and edits it where it sits — click a heading and type, swap an image, reorder sections — because the editor is the renderer. There's no API round trip and no separate dashboard to map mentally back onto the page.

Neither is wrong. But for a content site handed to a non-technical client, "edit the thing you're looking at" is a meaningfully smaller gap to close than "edit a form in an admin and go check the page."

The honest catch

For apps and content APIs, Payload is the right call.

The flip side of "no API layer" is real. If you're building an actual web application, need a content API feeding mobile clients or many channels, or want deep access control, auth, and hooks over structured data, Payload's architecture is the correct one — the API and admin you'd see as overhead for a brochure site are exactly what you want when content is a service. Primo is built for content sites, not apps, and won't pretend otherwise.

And today Primo's blocks are Svelte, not framework-agnostic the way an API is. On the roadmap is primo integrate <framework> — layering Primo onto an existing SvelteKit app (Astro next, Next.js aspirational). If you need React or a neutral API right now, weigh that honestly.

Questions developers ask

Isn't Payload's code-first schema the same idea as Primo's fields.yaml?

They’re cousins, but they end in different places. Both put the schema in source you version-control — Payload in TypeScript config, Primo in a fields.yaml beside the block. The difference is what the schema produces: Payload generates a separate admin and serves content over an API your app fetches; Primo generates an editor that runs on the rendered page itself, reading the same files the renderer does. No API in between.

Do both work with AI agents like Claude Code?

Yes. Payload’s schema and config are TypeScript files an agent can edit well, and its types are excellent. Primo’s difference is that an agent editing a block’s code and a client editing that block on the page are touching the same files — there’s no API layer or separate admin between what the agent changes and what the client sees.

Can Primo serve content to a mobile app or multiple front-ends like Payload?

Not the way Payload does. Payload’s API is built precisely to feed many channels at once, and if that’s your need it’s the honest pick. Primo is for a single content site where the editor and renderer are the same surface. If you need content-as-a-service across mobile and web, choose Payload.

Both are MIT and self-hostable — so what's the real trade?

Architecture, not openness. Both are MIT and self-hostable, so ownership isn’t the question. Payload gives you a code-first backend with an API and admin — ideal for apps and structured content. Primo gives you one source of truth with no API layer and in-place editing — ideal for content sites. Primo’s content lives in SQLite (via PocketBase), code in your repo, and primo pull exports both.

Build it with an agent. Hand it to a human.

One command to spin up a workspace. Point any agent at the repo, then hand your client the browser.

$ npx primo-cli init my-workspace
✓ workspace ready · server.yaml written

MIT · open source · free forever