Your content as flat JSON files. Render it with any language, any framework, any runtime.
The big idea
@webhouse/cms stores content as flat JSON files. Not in a database. Not behind an API. Just files in a directory:
content/
posts/
hello-world.json
hello-world-da.json
my-second-post.json
pages/
about.json
contact.json
globals/
site.jsonEvery language that can read a file can consume this content. PHP, Python, Ruby, Go, C#, Rust, Elixir, Haskell, Bash — they all speak JSON.
Why this matters
Most CMS platforms lock you in. Contentful requires their SDK. Sanity wants their GROQ queries. WordPress needs PHP + MySQL. Strapi runs its own Node server. If you want to switch stacks, you export, migrate, and pray.
With @webhouse/cms, your content is files in your git repository. No lock-in. No migration. No vendor dependency. If you want to replace @webhouse/cms tomorrow with a different admin UI, your content is already in a portable format.
What's TypeScript-specific
Only the admin layer:
cms.config.ts— the schema definition (TypeScript)- Admin UI (Next.js)
- AI agents (TypeScript)
- Optional
@webhouse/cmsNext.js helpers
That's it. Everything below the admin layer is framework-agnostic.
What's framework-agnostic
| Component | Format | Consumable by |
|---|---|---|
| Content | JSON files | Anything that reads files |
| Media | Image files in public/uploads/ | Any web server |
| Schema | Exportable as JSON Schema (schema export →) | Any JSON Schema library |
| SEO | sitemap.xml, robots.txt, llms.txt | Standards-compliant crawlers |
| MCP | Public read-only MCP server | Any AI agent |
The content format
Every document follows the same structure:
{
"slug": "hello-world",
"status": "published",
"locale": "en",
"translationGroup": "uuid-shared-with-translations",
"data": {
"title": "Hello, World!",
"content": "## Welcome\n\nThis is my first post.",
"date": "2026-04-08",
"tags": ["intro", "hello"]
},
"id": "unique-id",
"_fieldMeta": {}
}To filter published content, skip anything where status !== "published". To read only one language, filter by locale.
Reading content from any language
The pattern is always the same:
- List files in
content/<collection>/ - Parse each JSON
- Filter where
status === "published" - Optionally filter by
locale - Sort / render as needed
See the framework-specific guides:
- Consume from Laravel (PHP)
- Consume from Django (Python)
- Consume from Rails (Ruby)
- Consume from Go
- Consume from C# / .NET
The admin stays the same
Regardless of which framework you use to render the site, the CMS admin UI is the same Next.js application. Editors log in, edit content, hit publish — and the JSON files update in your git repository.
What about building?
F126 (planned) will let CMS admin invoke ANY build command — php artisan build, hugo --minify, bundle exec jekyll build, python manage.py collectstatic — not just our native TypeScript pipeline. Today you can still trigger builds via git hooks, CI/CD, or your own scripts.
Schema export
Non-TypeScript runtimes can't execute cms.config.ts, so the CMS exports a JSON Schema document (webhouse-schema.json) that describes the content model in a language-agnostic way. Reader libraries use this file to introspect collections, generate types, and validate documents.
Generate it from the CMS admin UI (Site Settings → Schema export → Save to project root) or from the CLI:
npx cms export-schema --out webhouse-schema.jsonRead the full schema export guide →
Trade-offs
File-based content is not for everyone. Consider:
- Scale — thousands of documents work fine. Millions do not. Use a database adapter if you need scale.
- Concurrent writes — filesystem adapter is single-writer. If you need real-time multi-editor, use the Supabase adapter.
- Search — no built-in full-text search. Use a separate search index (Algolia, Meilisearch, Typesense).
For 95% of content-driven sites, file-based content is faster, simpler, and safer than any database.
Next steps
- Read Introduction for the overall architecture
- Pick your framework from the consumer guides above
- Look at the examples for working code