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.
…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.
…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
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.
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.
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."
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.