Build content-driven apps without managing another backend.
Define your content in code, deploy everything to a single Cloudflare Worker,
and edit locally with changes only published when you're ready.
[!WARNING] baseConfig is under active development.
The project is currently in **0.x**. While the core is functional, APIs,
field types, and behavior may change before the
first stable release. It's not yet recommended for production use.
-
⚡ Edge-native Runs entirely on Cloudflare. Your website, CMS, API, and media library deploy together as a single Worker.
-
💾 Local-first editing Write without worrying about your connection. Changes stay on your device until you choose to publish.
-
🧩 Config-driven Describe your collections, globals, and fields in TypeScript. Your configuration becomes your CMS.
-
📦 Batteries included Collections, globals, rich text, blocks, relationships, media, authentication, and plugins in one place.
-
🔌 Built to extend Create your own fields, blocks, endpoints, hooks, and plugins without fighting the framework.
Starting a brand new project:
bunx @baseconfig/cli my-appFollow the prompts (project name, D1/R2/KV resource names), then:
cd my-app
bun run local # generate schemas + migrate a local D1
bun run devAdding baseConfig to an existing TanStack Start project instead:
bun add @baseconfig/core @baseconfig/ui hono drizzle-orm better-authDefine your content model, mount the handler, and you're ready to go.
export default baseConfig({
collections: [...],
globals: [...],
})That's it.
- Content API - Mounted to /api/$.ts
- Admin dashboard - Mounted to /admin or whatever path you prefer
- Media library - Pass the binding
- Authentication - But based on your own better auth setup
- File uploads - Fully setup to work with R2
- Storage - Pass R2 Binding
- Dashboard - Covered in admin/$.ts catch all
All running from the same Cloudflare Worker.
- ~20 field types (text, textarea, richtext, checkbox, switch, date, keywords, upload, select, combobox, radio, email, number, password, confirmPassword, hidden, code, json, slug, point), plus composite types (array, blocks, relationship, relations, meta, menu, links) and layout-only types (row, collapsible, group, tabs-as-field, ui)
- Real D1-backed content persistence, one table per collection/global
- Local-first drafting (edits live in
localStorageuntil published) - 7 built-in page blocks (richtext, media, cta, banner, grid, code, relatedPosts)
- Plugin system (
endpointFactories/hooks/blocks) with a reference plugin (@baseconfig/plugin-form-builder) - R2-backed media library
- Project-scaffolding CLI (
@baseconfig/cli), with two reference templates (basics, a minimal end-to-end app;pharmacy, a fuller e-commerce-style example) - Generated per-collection/global TypeScript types (
base.types.ts), givingbase.find/findByID/findGlobalfull type safety - Field pruning: reconciles stored data against the current schema, dropping anything left over from a renamed/removed field (an explicit admin "Prune" action, plus automatic reconciliation of local drafts)
- Collection/global-level access control (Payload-style
access: {create, read, update, delete}functions per collection,{read, update}per global; unset means open, matching Payload's own default) - Local API parity:
createLocalAPI()(@baseconfig/core/api), an in-processfind/findByID/create/update/delete/prune/findGlobal/updateGlobal/pruneGlobalclient for server functions/loaders in the same Worker, no HTTP round-trip,overrideAccessdefaulting to trusted (matching Payload's own Local API) -
joinfield (virtual, reverse-relationship queries) - Field-level
unique/index(blocked on every field currently living in one opaquedataJSON column rather than its own SQL column) - Field-level conditional visibility (show/hide a field based on a sibling field's value)
-
virtualfields (computed, not stored) - Field-level
validate/hooks/access(collection/global-level access is done, see above; field-level is a distinct, still-open piece) - A sidebar layout option for the document editor
