A very naive and opinionated, developer-first headless CMS for every modern TypeScript framework. Self-hostable and free.
Human x LLM build note: cms0 has been highly co-written by Jeff and LLMs, both paid and free. The goal is still very human: keep content modeling close to application code, keep the editor usable, and keep the runtime understandable.
cms0 is a code-first headless CMS for TypeScript projects. You describe your content shape in code, publish that shape into a self-hosted admin runtime, edit content in the admin UI, and read the result from your app through typed accessors.
The current core repository contains the self-hostable product: the SDK and CLI, the admin app, the docs app, reusable runtime packages, shared UI, and release tooling.
cms0 keeps the content contract in your application code and lets the admin runtime build editing and API behavior from that contract.
flowchart LR
A["Your TypeScript app"] --> B["@cms0/cms0 schema + typed client"]
B --> C["cms0 dev / cms0 build"]
C --> D["Schema descriptor"]
D --> E["@cms0/admin runtime"]
E --> F["Postgres"]
E --> G["Filesystem or S3 storage"]
E --> H["Admin editor UI"]
H --> F
B --> I["Typed reads in your app"]
I --> J["/api/content runtime API"]
J --> E
The loop is:
- Define content types with TypeScript.
- Run
cms0 devlocally orcms0 buildin CI. - cms0 publishes a schema descriptor to
@cms0/admin. - Editors update content in the admin UI.
- Your app reads saved content through
@cms0/cms0.
packages/cms0: public SDK and CLI package.apps/admin: self-hosted Next.js admin app and runtime API host.apps/docs: public documentation site.packages/admin-server: admin server routing, adapters, graph/runtime behavior, backups, data transfer, and triggers.packages/admin-client: typed client helpers and React Query hooks for admin server APIs.packages/admin-contract: shared request and response contracts.packages/shared: descriptor, graph query, storage, email, and common utility contracts.packages/auth: reusable auth helpers, permissions, session defaults, and auth factory building blocks.packages/ui: shared shadcn-based primitives and reusable admin UI.packages/transactional: provider-agnostic transactional email helpers.packages/api-docs: OpenAPI generation helpers.packages/db-schema-ops: schema push and database operation helpers.examples/reproduce-bug: local SDK smoke and reproduction workspace.
Requirements:
- Node.js 22 or newer.
- pnpm 10.
- PostgreSQL.
Install dependencies:
pnpm installCreate local environment files:
cp apps/admin/.env.example apps/admin/.env.local
pnpm generate:docs-envEdit apps/admin/.env.local, especially:
DATABASE_URLBETTER_AUTH_SECRETCMS0_PUBLIC_APP_URLBETTER_AUTH_URLTRUSTED_ORIGINSADMIN_EMAILADMIN_PASSWORDORG_NAME- storage and email variables
Run the admin and docs:
pnpm dev:admin
pnpm dev:docsThe default local admin origin is http://localhost:3000. The runtime API base URL is:
http://localhost:3000/api/contentThe repository includes a local-first Docker Compose stack for the self-hosted admin and Postgres.
cp deploy/docker/admin.env.example deploy/docker/admin.envEdit deploy/docker/admin.env, then run:
pnpm docker:admin:build
pnpm docker:admin:upThe admin runs at http://localhost:3000. The stack uses named Docker volumes for Postgres data and admin storage, so uploads, snapshots, and backups survive container restarts.
Check readiness with:
curl http://localhost:3000/api/healthRead deploy/docker/README.md and the self-hosting deployment docs before using Docker outside local development.
Install the SDK in your TypeScript app:
pnpm add @cms0/cms0Create a typed cms0 entry:
import { cms0 } from "@cms0/cms0";
type RootSchema = {
homePage: {
headline: string;
};
};
export const cms = cms0<RootSchema>({
apiConfig: {
baseUrl: process.env.CMS0_API_BASE_URL,
key: process.env.CMS0_API_KEY,
},
});Create cms0.config.ts next to your app package:
import "dotenv/config";
import { defineConfig } from "@cms0/cms0/config";
export default defineConfig({
entry: "./src/cms0.ts",
api: {
baseUrl: process.env.CMS0_API_BASE_URL,
key: process.env.CMS0_API_KEY,
},
});Publish the schema:
pnpm exec cms0 devThen open the admin UI, edit content, and read it back:
const homePage = await cms.homePage();
console.log(homePage.headline);- Start here: full first loop from schema to edited content.
- Self-hosting: admin runtime setup, environment, deployment, and operations.
- App integration: wiring
@cms0/cms0into an app. - Content modeling: schema shape and modeling guidance.
- Reference: runtime and package reference pages.
- Troubleshooting: common setup and runtime problems.
- License: workspace license map and standard license text locations.
Run the docs locally with:
pnpm dev:docspnpm dev:admin: run the self-hosted admin.pnpm dev:docs: run docs on port3008.pnpm typecheck: run TypeScript checks through Turbo.pnpm test: run unit and integration tests.pnpm build: build packages and apps.pnpm test:e2e: run the admin Playwright suite.pnpm docker:admin:build: build the local Docker admin image.pnpm docker:admin:up: start the Docker admin and Postgres stack.pnpm docker:admin:down: stop the Docker admin and Postgres stack.pnpm changeset: create a release changeset for publishable package changes.pnpm verify:publish: pack and inspect publishable package tarballs.
cms0 uses standard open-source licenses per workspace.
Each app, example, and package has its own LICENSE file. npm packages also declare the same license in package.json.
- SDK, client, shared contract, UI, email, docs, and API helper workspaces use the Apache License 2.0.
apps/admin,@cms0/admin-server, and@cms0/db-schema-opsuse the GNU Affero General Public License v3.0 or later.
See LICENSE for the workspace map.
cms0 is early, opinionated, and still being shaped. Contributions are welcome when they make the core product clearer, safer, easier to run, or easier to build with.
Read CONTRIBUTING.md before opening a pull request.
Contributors should create their own feature or fix branch from the latest main, then open a pull request back into main. Do not work directly on main unless you are a maintainer doing repository coordination.
If you use an AI coding assistant, also read .agents/AI_CONTRIBUTING.md. Human-facing project rules stay in this README and CONTRIBUTING.md; assistant-specific workflow rules live under .agents.
The publishable package workspaces are managed with Changesets. Apps under apps/*, including @cms0/admin and @cms0/docs, are private deployables for now and are not published to npm from this repository.