mdcms.config.ts using TypeScript and Zod, then sync the schema to your server. The schema drives Studio form generation, content validation, and API behavior automatically — there is no manual schema editor in Studio.
Basic syntax
UsedefineType() to create a content type and defineConfig() to register it:
mdcms.config.ts
Type options
Each type accepts the following options:| Option | Type | Required | Description |
|---|---|---|---|
directory | string | Yes | Filesystem path where documents of this type are stored. Must be covered by contentDirectories. |
localized | boolean | No (default: false) | Whether documents of this type support per-locale variants. Requires locales config when true. |
fields | Record<string, ZodSchema> | Yes | Map of field names to Zod schemas. Each field becomes a frontmatter property and a Studio form control. |
Field types
Every Zod type maps to a specific Studio form control. MDCMS serializes each field into a schema registry snapshot that drives both server-side validation and client-side rendering.Primitives
| Zod schema | Registry kind | Studio control | Notes |
|---|---|---|---|
z.string() | string | Text input | Base type for most text fields |
z.number() | number | Number input | Supports .int(), .min(), .max() checks |
z.boolean() | boolean | Toggle switch | Commonly paired with .default(false) |
z.coerce.date() | date | Date picker | Coerces string values to Date objects |
Complex types
| Zod schema | Registry kind | Studio control | Notes |
|---|---|---|---|
z.array(z.string()) | array (item: string) | Tag input | Repeatable string entries |
z.array(z.number()) | array (item: number) | Repeatable number inputs | |
z.object({...}) | object | Nested fieldset | Each property rendered recursively |
z.enum(["a", "b", "c"]) | enum | Dropdown select | Options extracted from enum entries |
z.literal("x") | literal | Hidden / read-only | Fixed value, useful for discriminators |
Modifiers
Modifiers control whether fields are required, nullable, or have defaults. They affect both the schema registry snapshot and Studio form behavior.| Modifier | Effect | Registry impact |
|---|---|---|
.optional() | Field can be omitted entirely | required: false |
.nullable() | Field can be explicitly set to null | nullable: true |
.default(value) | Provides a default value; field becomes optional | required: false, default: <value> |
A field with
.default() is implicitly optional in the schema registry. The
default value is stored in the registry snapshot and applied during
validation.Validation
Zod check methods are serialized into the schema registry aschecks entries. They run both server-side (on write) and in Studio (on form input).
Common validators
Available checks by type
String checks
String checks
| Method | Description | |---|---| |
.min(n) | Minimum length | | .max(n)
| Maximum length | | .email() | Valid email format | | .url() | Valid URL
format | | .regex(pattern) | Matches regular expression (pattern and flags
are serialized) |Number checks
Number checks
| Method | Description | |---|---| |
.min(n) | Minimum value | | .max(n) |
Maximum value | | .int() | Must be an integer |Array checks
Array checks
| Method | Description | |---|---| |
.min(n) | Minimum array length | |
.max(n) | Maximum array length |Registering types
Add your types to thetypes array in defineConfig():
mdcms.config.ts
Schema hash
Every write operation to the MDCMS API requires anx-mdcms-schema-hash header that matches the currently synced schema. This prevents writes against a stale schema.
- The CLI and SDK handle the schema hash automatically.
- If the hash doesn’t match, the server returns a
SCHEMA_HASH_MISMATCHerror. - Re-run
mdcms schema syncto update after config changes.
Environment overlays
Environment overlays let you customize the schema per environment without duplicating type definitions. Define them in theenvironments config:
mdcms.config.ts
Overlay operations
| Operation | Description |
|---|---|
add | Add new fields that don’t exist on the base type. Errors if the field already exists. |
modify | Replace the schema for an existing field. Errors if the field doesn’t exist. |
omit | Remove fields by name. Errors if the field doesn’t exist. |
extends | Inherit from another environment instead of the base types. Supports chaining. |
Field-level environment targeting
For simpler cases, you can use the.env() modifier directly on field definitions to target specific environments:
.env() are only included in the specified environments. This is syntactic sugar that gets expanded into add overlays during config parsing.
Complete example
A full configuration with multiple types, localization, validation, references, and environment overlays:mdcms.config.ts