Using Keystatic
Keystatic is a file-based CMS that stores content in JSON files locally or syncs with GitHub. It's the default CMS in Makerkit.
Keystatic stores your content as JSON files - either locally on disk or synced to a GitHub repository. It's the default CMS in Makerkit because it requires zero external setup: content lives in your codebase, version-controlled alongside your code. For collaborative editing or edge deployments, switch to GitHub mode to enable remote content access.
Keystatic is a file-based, Git-native CMS that stores content as JSON files and provides a built-in admin UI for editing.
- Use local mode when: you're a solo developer, deploying to Node.js environments, and want the simplest setup with no external dependencies.
- Use GitHub mode when: you need team collaboration, want content changes to trigger CI/CD pipelines, or are deploying to edge runtimes (Cloudflare, Vercel Edge).
This page is part of the CMS Integration documentation.
Storage Modes
| Mode | Content Location | Best For | Edge Compatible |
|---|---|---|---|
local | Local filesystem | Solo development | No |
github | GitHub repository | Team editing, edge deploys | Yes |
cloud | Keystatic Cloud | Managed hosting | Yes |
Local Mode (Default)
Local mode stores content in your project's filesystem. No external services required.
CMS_CLIENT=keystaticNEXT_PUBLIC_KEYSTATIC_STORAGE_KIND=localKEYSTATIC_PATH_PREFIX=apps/webNEXT_PUBLIC_KEYSTATIC_CONTENT_PATH=./contentContent is stored in the content directory and committed to your repository.
GitHub Mode
GitHub mode syncs content to a GitHub repository, enabling team collaboration and edge runtime compatibility.
CMS_CLIENT=keystaticNEXT_PUBLIC_KEYSTATIC_STORAGE_KIND=githubNEXT_PUBLIC_KEYSTATIC_STORAGE_REPO=your-org/your-repoKEYSTATIC_PATH_PREFIX=apps/webNEXT_PUBLIC_KEYSTATIC_CONTENT_PATH=./contentKEYSTATIC_GITHUB_TOKEN=github_pat_xxxxxxxxxxxxxxxxxxxxGitHub Token
Generate a personal access token from GitHub Developer Settings with contents: read permission on your repository.
- Go to GitHub → Settings → Developer settings → Personal access tokens
- Generate new token (fine-grained)
- Select your repository
- Grant
Contents: Read-onlypermission - Copy the token to
KEYSTATIC_GITHUB_TOKEN
GitHub App for Admin UI
The Keystatic admin UI in GitHub mode requires a GitHub App installation for authentication. See the Keystatic GitHub Mode documentation for setup instructions.
Keystatic Cloud Mode
For managed hosting without GitHub configuration:
CMS_CLIENT=keystaticNEXT_PUBLIC_KEYSTATIC_STORAGE_KIND=cloudKEYSTATIC_STORAGE_PROJECT=your-project-idAdding the Admin UI
Run the generator to add the Keystatic admin routes to your app:
turbo gen keystaticThis creates a /keystatic route where editors can manage content.
Production Security
The Keystatic admin at /keystatic is publicly accessible by default. For production deployments, add authentication middleware or restrict access via your hosting provider's configuration.
Cloudflare Workers Compatibility
Cloudflare Workers doesn't send the User-Agent header that GitHub's API requires. Add this workaround to packages/cms/keystatic/src/keystatic-client.ts:
// Cloudflare Workers User-Agent fixconst originalFetch = globalThis.fetch;globalThis.fetch = (input: RequestInfo | URL, init?: RequestInit) => { return originalFetch(input, { ...init, headers: { ...init?.headers, 'User-Agent': 'Cloudflare-Workers', }, });};Common Pitfalls
- Local mode on edge runtimes: Cloudflare Workers and Vercel Edge Functions can't access the local filesystem. Use GitHub or Cloud mode for edge deployments.
- Missing GitHub token permissions: The token needs
contents: readon your repository. Without it, content fetching fails silently or returns empty results. - Admin UI exposed in production: The
/keystaticroute is public by default. Add authentication or remove the route in production if you don't need browser-based editing. - KEYSTATIC_PATH_PREFIX mismatch: If your content folder isn't at
apps/web/content, updateKEYSTATIC_PATH_PREFIXto match your monorepo structure. - Forgetting to commit content changes: In local mode, content changes are local files. Commit and push them to persist changes across deployments.
Frequently Asked Questions
How do I migrate from local to GitHub mode?
Can I use Keystatic with Cloudflare Pages?
Where is content stored in GitHub mode?
How do I add custom fields to content types?
Can multiple editors work simultaneously?
Next: WordPress →