Open-source project
Thinkmill/keystatic avatar
Thinkmill/keystatic

Keystatic: content that lives in GitHub, and a README that tells you to skip the repository

First class CMS experience, TypeScript API, Markdown & YAML/JSON based, no DB

2,444 stars168 forksTypeScriptMIT

At a glance

What is it?
Keystatic is Thinkmill's TypeScript CMS, which keeps content as Markdown and YAML or JSON, writes straight to GitHub and needs no database. Its own README calls the project experimental and routes readers to the hosted guides and the CLI rather than to the source it ships.
Who is it for?
Keystatic is a sensible fit for a team that already keeps content in the same repository as its code and wants a CMS without a database to operate. Before adopting it, read the Status section rather than the package description, because the two disagree about how finished this is.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 10 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The README points readers away from the repository itself

The Status section is close to the top of the file, and it is candid. It says the team is working on it, that things are experimental at the moment, and that readers should check keystatic.com to try it and to look at the growing collection of templates. The Developing Keystatic section then names three ways in: follow the guides, use the CLI, or run the monorepo. The third option is qualified in the same paragraph, because the dev projects inside the repository are intended for testing and for developing features, and are described as not being great examples of actual Keystatic projects.

That is an unusual admission to make about your own source tree, and it narrows what the checkout is for. Example projects do exist in-tree, under a templates/ directory, yet the guidance still sends people to the web documentation rather than to those files. Feedback is invited through GitHub Discussions, and a mailing list is offered for updates.

First class in the description, experimental in the status

The repository description reads First class CMS experience, TypeScript API, Markdown & YAML/JSON based, no DB. A short way further down the same file, things are described as experimental at the moment, and readers are invited if they are curious and brave. Both statements ship together and nothing in between reconciles them.

The technical claims are more precise than the pitch. Markdown and YAML or JSON files are the storage layer, GitHub is the backend, and no database sits between them. The README says the project was conceived for front-end frameworks such as Next.js, Remix and Astro, and that it is built with DNA from Keystone. What it does not give is a version number for the product itself, a list of which framework adapters ship in this repository, or any statement about the stability of the stored file format, which is the detail that matters most to anyone planning to keep content in it for years.

cd dev-projects/{example} is a template, not a command

The local development instructions are three lines.

sh
pnpm install
cd dev-projects/{example}
pnpm run dev

The middle line is a placeholder. The braces are not shell expansion, so pasted exactly as printed the command fails, and the text never says which names the dev-projects directory holds or how many of them exist. A reader has to list that directory before the sequence works.

The requirements are given as node.js v18 and pnpm, with no version number attached to either. A pnpm error is handled with a specific instruction: make sure corepack is in use, uninstall pnpm, and then run corepack enable pnpm.

code
corepack enable pnpm

Treating a correctly installed pnpm as the thing to remove is a deliberate choice, and it is the same reason the package manager gets pinned to an exact patch release elsewhere in the repository.

engines names pnpm and stays silent on node

The root package.json pins the package manager twice and the runtime not at all.

json
"packageManager": "[email protected]",
"engines": {
  "pnpm": "11.20.0"
},

The same exact patch version appears in both places, so contributors and automated installs resolve to one release, and corepack will refuse anything else. The node requirement lives only in README prose, where nothing can enforce it, which means a contributor on a newer runtime finds out at build time rather than at install time.

The root package itself is named keystatic-repo, is marked private, and carries version 1.0.0. That number is a placeholder for a workspace root and has no relationship to any published version of the CMS, so it is worth ignoring when reading the file as a release record.

Versions move through changesets and the releases page stays empty

The repository has no GitHub releases, and that follows from how it publishes. The root scripts include version-packages running changeset version, and a release entry that builds the packages, runs a prepare-publish script, and then calls changeset publish. A .changeset/ directory sits at the root, which is where pending version bumps accumulate between releases. The result is that version history lives on the package registry rather than on the releases page, so someone browsing the repository sees a stream of commits with no tagged milestones beside them.

Publishing is also a manual three step operation rather than something a continuous integration job does from a tag. The build step it depends on changes into packages/keystatic, runs a setup script there, returns to the root and calls preconstruct build, so a release depends on a working local clone as much as on the registry credentials.

postinstall chains four build steps before anything runs

Installing dependencies in this monorepo is not a passive operation. The postinstall hook runs preconstruct dev, then manypkg check, then a design-system localization build, then a setup step inside packages/keystatic.

json
"postinstall": "preconstruct dev && manypkg check && node design-system/pkg/build-l10n.mts && cd packages/keystatic && pnpm run setup",

Four things execute on a fresh clone, before a single package has been built, and the same pattern repeats in the package build script. There is no conditional around the chain, so a failure anywhere in it stops the install rather than leaving a half-prepared workspace. That is the safer default, and it also means a developer cannot inspect a broken tree without first bypassing the hook.

fresh starts by deleting ignored and untracked files

Two scripts deserve reading before they are run. clean is git clean -fXd, which removes ignored files and untracked directories, and fresh chains npm and pnpm together.

json
"clean": "git clean -fXd",
"fresh": "npm run clean && pnpm install",

Run from a working tree that holds untracked work, clean removes that work without asking. And fresh reaches for npm in a repository whose own package manager field says pnpm, which works only because the clean script it calls happens to run under whatever shell npm selects.

The same file is more careful elsewhere. Validation is written as a glob over script names, `pnpm run '/check:*/'`, so a newly added check: script is picked up with no edit, and there is a strict test mode that sets an environment variable inline, which is portable across the shells most contributors use and not inside cmd.exe.

Copyright reads 2023 and two domains are in circulation

Two small inconsistencies are easy to trip over. The licence block reads Copyright (c) 2023 Thinkmill Labs Pty Ltd, while the default branch was last pushed on 2026-10-01, so the copyright line has sat unchanged for three years next to an MIT LICENSE file in a repository that publishes package versions on a schedule.

The project also answers to two web addresses. The header block at the top of the README links to keystatic.thinkmill.com.au, while the Status section sends readers to keystatic.com, the guides link points at the same domain, and the repository homepage field records keystatic.com as well. Both hosts are presented as the project without a note explaining the split, which is a small thing to fix and a confusing one to hit while looking for the documentation the README keeps recommending.

Editorial conclusion

Keystatic is a sensible fit for a team that already keeps content in the same repository as its code and wants a CMS without a database to operate. Before adopting it, read the Status section rather than the package description, because the two disagree about how finished this is. Check whether the commit based file format suits a long lived content archive, and plan for a monorepo toolchain pinned to one exact pnpm patch release. Contributors should read the clean and fresh scripts before running either, since the first deletes ignored and untracked files without confirmation.

Frequently asked questions

What is Keystatic?

A CMS from Thinkmill whose description calls it a first class CMS experience with a TypeScript API, Markdown and YAML or JSON based, and no database. It connects directly to GitHub and was built with DNA from Keystone, and it was conceived for front-end frameworks such as Next.js, Remix and Astro. The README also states plainly that things are experimental at the moment.

How does Keystatic work?

Content is stored as Markdown or YAML and JSON files rather than in a database, and the CMS connects straight to GitHub, so content lives in the same place as the source code. The editing interface is reached through a CLI, through the guides hosted at keystatic.com, or through the React interface in the web front end, and the repository itself ships templates for several frameworks.

how to use keystatic

The README steers you to the guides at keystatic.com or to the CLI, and explicitly says the dev projects in the repository are meant for testing and are not good examples. To run one in the monorepo you need node.js v18 and pnpm, then pnpm install, cd into a dev-projects entry and run pnpm run dev. If pnpm misbehaves, uninstall it and run corepack enable pnpm.

is keystatic free

The code is MIT licensed, with the copyright line reading Copyright (c) 2023 Thinkmill Labs Pty Ltd. The README does not say anything about a hosted version or what one would cost, and it points readers at keystatic.com and a mailing list for news instead. The package itself is published to a registry rather than through GitHub releases.

keystatic vs keystone

The repository does not set out a feature comparison, and it does not need to lean on one: the README says Keystatic is built with DNA from Keystone and that it connects directly to GitHub, describing it as a CMS with a TypeScript API over Markdown and YAML or JSON files and no database. Anything beyond that sentence about how the two differ would have to come from elsewhere.

Official sources

  1. Issues
  2. License: MIT
  3. Project website
  4. README
  5. Thinkmill/keystatic on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/thinkmill-keystatic.svg)](https://hysenlabs.com/projects/thinkmill-keystatic)