Content modeling that gets out of your way

Flexible schemas, type-safe APIs, and an editor your team will actually use.

Built different

No API Metering
Fetch content as often as you need. No usage-based pricing.
Unlimited Locales
Add every language. Fallback chains included. No per-locale fees.
Your PostgreSQL
Content lives in your database. Query with SQL. Export with pg_dump.
No Vendor Lock-In
Open source. Standard SQL format. Leave anytime.

Core capabilities

Flexible Content Modeling
11 field types. Validations. Links. Arrays. Rich text. Design your schema, not fill templates.
Multi-Locale
Fallback chains. Per-field localization. Unlimited languages.
Type-Safe API
tRPC with full TypeScript types. IDE autocomplete for your content.
Self-Host Anywhere
Docker, Vercel, Railway, Fly.io. One container, your infrastructure.

Model content your way

Define custom content types with the fields you need. Build complex relationships without fighting the system.

11 Field Types
  • Symbol & Text for short/long content
  • RichText with embedded entries & assets
  • Numbers, booleans, dates, locations
  • Link references to entries & assets
  • Arrays of any type
  • Object for arbitrary JSON
Built-in Validation
  • Size constraints (text length, array size)
  • Number and date ranges
  • Regex pattern matching
  • Enum value restrictions
  • Unique field values
  • Asset size & dimensions

Production-ready editing

Your content team will feel at home from day one.

Rich Text Editor
TipTap-powered WYSIWYG. Headings, lists, quotes, code blocks. Embed entries and assets inline.
Asset Management
Drag-drop uploads. Automatic image optimization. 10 responsive sizes generated.
Locale Comparison
Side-by-side editing. Copy content between languages. Per-field localization control.
Draft & Publish
Per-locale publish states. See what's live vs. in progress. Webhooks on publish.

Type-safe from database to IDE

tRPC gives you autocomplete for your content. No codegen required.

// Query with filters and link resolution
const posts = await cms.entries.list({
  schemaId: "blog-post",
  locale: "en",
  where: { featured: { eq: true } },
  limit: 10,
});

// Resolve linked content (author, category, images)
const resolved = await cms.entries.resolve(posts[0], { depth: 2 });

resolved.author.name      // Resolved Author entry
resolved.author.avatar    // Resolved Asset with url, width, height
resolved.category.parent  // 2 levels deep

Ready to build?

Get started in minutes. Self-host free or join the cloud waitlist.