⚠️ Work in progress — createCMS is pre-1.0 and not production-ready (not tested in production). Expect breaking changes.
createCMS
Reference

Define API

defineCollection, defineBlock, and the helpers that build your content model.

These helpers are identity functions that give your definitions full type inference. They are exported from the package root (see packages/cms/src/core/define.ts).

defineCollection

Defines one collection: a root, optional child blocks, and optional slug behavior.

function defineCollection<TProps, TBlocks>(
  collection: CollectionDefinition<TProps, TBlocks>,
): CollectionDefinition<TProps, TBlocks>;
FieldTypeRequiredDescription
labelstringyesHuman label for the collection.
root{ properties }yesThe root block definition and its typed properties.
blocksRecord<string, BlockDefinition>noChild block definitions.
structureCollectionStructurenoPlacement rules keyed by parent block name (or 'root'). See Placement rules.
slugSlugConfignoURL path behavior (see below).
descriptionstringnoOptional description.
reusableBlockbooleannoEditor hint that this collection's roots are meant to be embedded via a reference property. Any collection can be a reference target regardless.

Placement rules: structure

structure declares which children each container may hold. It is keyed by parent block name — or the literal 'root' for the top level — and each entry is one of three mutually exclusive modes:

EntryModeMeaning
{} or { accepts: '*' }openHolds any block.
{ accepts: ['a', 'b'] }whitelistHolds only the listed blocks (fail-closed). excludes is forbidden here.
{ excludes: ['z'] }blacklistHolds anything except the listed blocks (fail-open).

Block names autocomplete against the collection's blocks and a typo is a compile error. Whether a block can contain children at all is the separate allowChildren flag on the block (the root always accepts children). Rules are enforced by the type checker, the editor's drop zones, and the server (BLOCK_NOT_ALLOWED_IN_PARENT). See Blocks → Nesting for the full explanation.

defineCollections

Groups collections into one record and validates reference targets at compile time: a reference property whose collection is not a key in the record is a type error.

const collections = defineCollections({ pages, posts });

defineBlock

Defines a block with typed properties. A block that declares events must include a trackingId property (enforced at compile time).

function defineBlock<TProps, TEvents>(
  block: BlockDefinition<TProps, TEvents>,
): BlockDefinition<TProps, TEvents>;
FieldTypeRequiredDescription
labelstringyesHuman label for the block.
propertiesRecord<string, BlockProperty>yesThe block's typed fields.
eventsRecord<string, EventDeclaration>noEvents a functional block can emit.
descriptionstringnoOptional description.
previewImageUrlstringnoOptional preview image.
groupstringnoEditor hint: the block-picker category this block is grouped under (e.g. 'Forms'). Presentational only.
allowChildrenbooleannoWhether the block can contain children at all. Which children it accepts is set on the collection's structure map.

defineRoot

Defines a root block on its own.

const pageRoot = defineRoot({
  properties: {
    title: { type: 'string', required: true, label: 'Page Title' },
  },
});

trackingId

Returns the reserved trackingId property to spread into a functional block's properties.

function trackingId(label?: string): { trackingId: { type: 'string'; label: string } };

defineAuthMiddleware

Identity helper for the auth middleware, for full inference. The middleware receives the request context and returns a result object; userId is optional (anonymous requests omit it).

Block properties

Every property is one of ten field types. The common shape:

{
  type: BlockPropertyType;
  label: string;
  required?: boolean;
  defaultValue?: unknown; // matches the field's type
  description?: string;
  placeholder?: string;
  group?: string; // editor hint: field-group (fieldset) in the property panel
}
typeValueExtra config
stringstringminLength?, maxLength?, pattern?
numbernumbermin?, max?
booleanboolean
datestring (ISO)
richTextstring (HTML)minLength?, maxLength?, pattern?
imagestring (asset reference)
selectone of options[].valueoptions: { label, value }[]
referencetarget rootId stringcollection: string
linkLinkValue (internal / external / email / phone)allowedKinds?, allowedCollections?
listordered array of of valuesof (required), min?, max? — see list properties

Constraints

The extra config on the text and number types is declarative validation, enforced server-side by the generated zod schema — not an editor hint.

FieldApplies toMeaning
minLength / maxLengthstring, richTextBounds on the string length.
patternstring, richTextA JS RegExp source string the value must match, e.g. '^[a-z-]+$'. Write the source, not /…/.
min / maxnumberInclusive bounds on the value.

The same constraints apply to list elements of those types (of: { type: 'string', maxLength: 24 }). On a list itself, min / max bound the array length instead.

list properties

A list holds an ordered JSON array whose every element matches of. A list of reference is a multi-reference: its elements are walked by the reference and usage machinery exactly like a single reference. A list has no defaultValue, and nested-object elements are deliberately out of scope.

FieldTypeRequiredDescription
type'list'yesDiscriminates the spec.
ofListElementSpecyesThe element descriptor (see below).
labelstringyesHuman label for the field.
requiredbooleannoWhether the property must be present.
minnumbernoMinimum number of elements (inclusive).
maxnumbernoMaximum number of elements (inclusive).
descriptionstringnoOptional description.
placeholderstringnoEditor placeholder.
groupstringnoEditor field-group hint, as on any other property.

of is { type } plus whatever extra config that type carries as a scalar — options for select, collection for reference, the constraints above for string / richText / number. Every single-value type may be an element except link.

const post = defineBlock({
  label: 'Post',
  properties: {
    tags: { type: 'list', label: 'Tags', of: { type: 'string', maxLength: 24 }, max: 10 },
    related: { type: 'list', label: 'Related', of: { type: 'reference', collection: 'posts' } },
  },
});

The spec types are exported for typing your own controls: ListBlockPropertySpec, ListElementSpec and ListElementType, together with SelectOption, StringConstraints and NumberConstraints, from @createcms/core (type-only imports, safe in any bundle).

Slug config

type SlugConfig =
  | { enabled: false }
  | {
      enabled: true;
      prefix: string;
      allowIndex?: boolean;
      normalize?: boolean;
      nested?: boolean;
    };

On this page