After looking at Sanity as a way to add a CMS to an Astro website, I came across Keystatic. Both offer an editing dashboard, but they start from a very different idea about where content should live.
Sanity stores documents on its own platform and Astro queries them. Keystatic works with the files in a project and with Git. It can give someone a visual editor while keeping Markdown, MDX, JSON or YAML in the repository.
That difference affects who can edit, how changes are reviewed, what gets deployed and which problems show up months later.
What Keystatic is
Keystatic is a file-based CMS. Instead of sending content to an external database, it works with files in the project. In local mode, it writes directly to the file system. In GitHub mode, changes become commits in a repository. It also has a Cloud mode that simplifies authentication and can extend collaboration.
For a developer, the appeal is immediate. Content remains versioned alongside code. A change can be reviewed, reverted with Git and used to build the site from the same files Astro already reads. There is no need to create a content API or query a separate database to show a post.
That does not make it the right CMS for every client. In GitHub mode, editors need write access to the repository. Keystatic Cloud reduces that friction, but changes the model and adds another service. A dashboard does not remove the decisions around permissions, reviews and publishing.
The difference from Sanity is where content lives
Sanity is a headless CMS. Content lives in its Content Lake and Astro retrieves it through GROQ queries. It suits teams that need to edit from a dashboard separate from the repository, or content that will feed more than one channel.
Keystatic does not start from a content database. You configure collections and fields, but it writes the result to files. In practice, this is the first difference I would look at:
| Question | Keystatic | Sanity |
|---|---|---|
| Where does content live? | In the project or its Git repository. | In Sanity’s platform. |
| How does it reach Astro? | Astro can read the files with Content Collections. | Astro queries documents with GROQ. |
| What appears in history? | Content and code changes in commits. | Changes inside the CMS, with its own states and permissions. |
| What fits best? | Sites with a Git workflow and content close to code. | Editorial teams, shared content or a clear editing-development split. |
There is no winner for every project. If content needs to remain a repository artifact, Keystatic has a clear advantage. If an editorial team needs to work without GitHub permissions or knowing the project structure, Sanity is usually more natural.
In my article about Astro and Sanity, I explain the decisions a decoupled CMS introduces. Keystatic solves a different problem: keeping a file-based workflow while avoiding the need to open a code editor to edit MDX.
I would not drop it into this portfolio unchanged
This site looks like an ideal Keystatic candidate because its posts already live in MDX. There is a real limitation before installing packages, though.
Every article contains Spanish and English in the same file, inside HTML blocks with a lang attribute. The build removes the language that does not belong to each route before publishing. Keystatic’s MDX field can read and write MDX, but its documentation says HTML tags are not supported inside that content.
Adding Keystatic here without changing the content model would therefore break an important blog convention. It would not be enough to create a dashboard. I would need to choose one of these paths:
- Separate Spanish and English files.
- Replace the HTML blocks with a structure the editor can represent.
- Keep bilingual posts outside Keystatic and use it only for another content type.
That check matters more than the initial demo. A CMS needs to respect how real content is published, not merely let you create a sample entry.
How I would start on a new project
On a project without that constraint, I would begin with a small collection and local storage. Keystatic provides an Astro integration and a configuration that describes where files live and which fields an editor can change.
import { collection, config, fields } from '@keystatic/core'
export default config({
storage: { kind: 'local' },
collections: {
posts: collection({
label: 'Posts',
slugField: 'title',
path: 'src/content/blog/*',
format: { contentField: 'body' },
schema: {
title: fields.slug({ name: { label: 'Title' } }),
publishedAt: fields.date({ label: 'Date' }),
body: fields.mdx({ label: 'Content' }),
},
}),
},
})Next, I would make the fields match Astro’s schema instead of creating two independent content models. If Astro expects title, summary, tags and date, Keystatic should collect the same data. Content Collections can then still validate the build.
Keystatic’s official Astro guide also adds React and Markdoc in its base example. If the project uses MDX, the configuration needs adapting and testing against real content. Its integration adds admin routes and uses Node APIs, so publishing the dashboard needs an Astro adapter and a host that can run server-side code. Adding it changes how a previously fully static website is deployed.
GitHub is not an implementation detail
Local mode is convenient for trying the tool or for one person working in the project. For someone to edit from the deployed website, GitHub mode requires setting up a GitHub App, storing environment secrets and giving collaborators repository access.
That can fit a technical team well. An editor publishes a branch, someone reviews the change and deployment follows the normal workflow. I would not assume that a small business wants every copy change tied to GitHub, though.
Sanity separates those worlds more clearly. The editorial team uses Studio and the development team maintains the frontend. Keystatic keeps the worlds together, which can be a benefit or friction depending on who uses the dashboard.
When I would choose each one
I would choose Keystatic when content already lives in files, Git is a normal part of the work and I want to give a small group an editor without losing that traceability. It can fit documentation, a technical blog, a product site maintained by developers or a team reviewing changes through pull requests.
I would choose Sanity when editors need independence from the repository, the content model is broad, several channels are involved, or previews and drafts are important to the editorial process.
I would add neither to a website that barely changes just to have a dashboard. In that case, MDX and Content Collections are still fewer things to maintain.
The useful question is not “Keystatic or Sanity”. It is who will change content, what they should be able to change, and what check that change needs to pass before production. From there, the tool stops being a trend and becomes a maintenance decision.