Headless CMS for TypeScript

Your content model. Defined in code.

Define your collections and globals in TypeScript. Stackpress turns your schema into a powerful CMS and gives your application fully typed access to the same content.

See how it works

collections.ts

1import { defineCollection } from 'stackpress/config'
2 
3export const posts = defineCollection('posts', {)
4 fields: {
5 title: { type: 'text' },
6 excerpt: { type: 'text' },
7 content: { type: 'richText' },
8 cover: { type: 'upload' }
9 }
10})

Generated fields

Titletext
Shipping types from schema
Excerpttext
Define once. Edit in the CMS. Query with types.
ContentrichText
BIUP

Stackpress infers a typed client from the same collection you edit.

Coverupload

cover-hero.jpg

image/jpeg · 248 KB

  1. Schema
  2. Stackpress
  3. Posts
  4. Typed content

Hover or tap a field to trace it

How it works

Schema → CMS → content → API

One model. Four surfaces. Nothing to keep in sync.

Define

1export const posts = defineCollection('posts', {)
2 fields: {
3 title: { type: 'text', required: true },
4 slug: { type: 'text', unique: true },
5 content: { type: 'richText' },
6 cover: { type: 'upload' },
7 author: { type: 'relation', to: 'authors' },
8 }
9})
10 
11// Same file. Same types. No codegen.

From schema

Code generates the content model.

The CMS is not configured separately. Field types, relations, and media come from the same defineCollection call your app already compiles.

  1. Code
  2. Generates
  3. Content model

collections.ts

1export const posts = defineCollection('posts', {)
2 fields: {
3 title: { type: 'text' },
4 slug: { type: 'text' },
5 excerpt: { type: 'text' },
6 content: { type: 'richText' },
7 cover: { type: 'upload' },
8 author: { type: 'relation', to: 'authors' }
9 }
10})

cms / collections / posts

Document

Titletext
Shipping types from schema
Slugtext
hello-world
Excerpttext
Define once. Edit in the CMS. Query with types.
ContentrichText
BIUP

Stackpress infers a typed client from the same collection you edit.

Coverupload

cover-hero.jpg

image/jpeg · 248 KB

Authorrelation
Maya Chenauthors

Types

One schema. End-to-end types.

The client you call from loaders is inferred from the same collection definition. Populate expands uploads and relations without losing TypeScript.

  1. Schema
  2. CMS
  3. API
  4. Application

routes/posts.$slug.tsx

1const [post] = await stackpress.find('posts', {)
2 where: { slug: { equals: 'hello-world' } },
3 populate: ['author', 'cover'],
4 limit: 1,
5})
6 
7return { post }

Post

inferred
1{
2 id: string
3 title: string ← from schema
4 slug: string
5 excerpt: string
6 content: RichText
7 cover: Media
8 author: Author
9}
  • title: string
  • content: RichText
  • cover: Media
  • author: Author

Media

Media that fits your stack.

Upload files, store metadata on a media collection, and serve them through your storage adapter — S3-compatible, or anything that implements the adapter.

  1. Upload
  2. Metadata
  3. Storage adapter
  4. File URL

cms / media

Relations

Content that knows its neighbors.

Relations are fields in the schema, not a separate graph product. Populate them in a query and TypeScript follows.

Developer experience

Keep your content model close to your code.

Collections, globals, and loaders live in the repo. Types come from the schema — not a dashboard export.

  • TypeScript-first
  • Schema in code
  • Fully typed queries
  • Relations & media
  • Server-side loaders
  • No duplicated models
1import { defineCollection } from 'stackpress/config'
2 
3export const posts = defineCollection('posts', {)
4 fields: {
5 title: { type: 'text', required: true },
6 author: { type: 'relation', to: 'authors' },
7 cover: { type: 'upload' },
8 },
9 access: { read: () => true },
10})

Architecture

Your app, a generated CMS, storage underneath.

Stackpress sits between TypeScript routes and your database. Media goes through the same adapters you already configure.

TypeScript App
Stackpress CMS
Content
Media
Storage / DB

Typed end to end

The stackpress client knows your schema. Populate expands uploads to URLs and relations to documents — without losing TypeScript.

Media that fits your stack

Upload to S3 (or your adapter), store metadata in the media collection, and serve files through a proxied /cms/file route.

Content infrastructure, without the abstraction tax.

Define your model in TypeScript. Give your team a powerful editing experience. Ship typed content wherever you need it.

Open the CMS