Blueprints
Blueprints define the content schema for your entries, globals, taxonomies, and forms. They describe which fields appear in the Control Panel, how those fields are organised into tabs and sections, and what validation and visibility rules apply.
A blueprint is a YAML file that maps directly to the editing experience — every tab, section, and field you configure becomes a real interface element in the Control Panel.
Configuration Reference
File Location
Blueprints live at resources/blueprints/{type}/{handle}.yaml where {type} is one of:
| Type | Path | Used By |
|---|---|---|
collections |
resources/blueprints/collections/{handle}.yaml |
Collection entries |
globals |
resources/blueprints/globals/{handle}.yaml |
Global data sets |
taxonomies |
resources/blueprints/taxonomies/{handle}.yaml |
Taxonomy terms |
forms |
resources/blueprints/forms/{handle}.yaml |
Form definitions |
Top-Level Structure
A blueprint consists of named tabs, each containing fields and optional sections:
tabs:
<tab_handle>:
display: <Tab Label>
fields:
- handle: <field_handle>
field:
type: <field_type>
# ...field config
sections:
<section_handle>:
display: <Section Label>
fields:
- handle: <field_handle>
field:
type: <field_type>
Tabs
Tabs divide the editing interface into logical groups. Each tab renders as a clickable tab in the Control Panel. The tab handle is used internally; the display value is shown to editors.
tabs:
content:
display: Content
fields:
- handle: title
field:
type: text
display: Title
required: true
- handle: body
field:
type: tiptap
display: Body
metadata:
display: Metadata
fields:
- handle: published_at
field:
type: date
display: Publish Date
- handle: status
field:
type: select
display: Status
options:
options:
- draft
- published
- archived
Tabs render in the order they appear in the YAML. Place the most-used fields in the first tab so editors land on them by default.
Sections
Sections provide visual grouping within a tab. They render as labelled groups with a heading, useful when a single tab contains many fields that benefit from logical separation.
tabs:
main:
display: Main
fields:
- handle: title
field:
type: text
display: Title
required: true
- handle: slug
field:
type: slug
display: URL Slug
required: true
sections:
seo:
display: SEO Settings
fields:
- handle: meta_title
field:
type: text
display: Meta Title
validate:
- max:60
options:
placeholder: Override the page title for search engines
- handle: meta_description
field:
type: text
display: Meta Description
validate:
- max:160
options:
placeholder: Brief description for search results
social:
display: Social Sharing
fields:
- handle: og_image
field:
type: asset
display: Social Image
options:
max_files: 1
- handle: og_description
field:
type: text
display: Social Description
validate:
- max:200
Fields defined directly under the tab appear first, followed by sections in definition order.
Field Configuration
Each field in a blueprint is defined with a handle (the storage key) and a field object containing its configuration.
Field Properties
| Property | Type | Required | Description |
|---|---|---|---|
type |
string |
Yes | One of the 18 field type identifiers |
display |
string |
No | Label shown in the CP. Auto-generated from handle if omitted |
required |
boolean |
No | Whether the field must have a value. Default: false |
default |
any |
No | Default value pre-populated on create forms |
validate |
string[] |
No | Array of validation rule strings |
options |
object |
No | Type-specific configuration options |
visibility |
object |
No | Conditional display rules |
Field Handle
The handle becomes the key in your content files. Use snake_case for handles:
- handle: featured_image # stored as featured_image in content YAML
field:
type: asset
display: Featured Image # shown to editors in the CP
Default Values
Set default values to pre-populate fields when creating new entries:
- handle: status
field:
type: select
display: Status
default: draft
options:
options:
- draft
- published
- handle: featured
field:
type: toggle
display: Featured
default: false
- handle: author_name
field:
type: text
display: Author
default: Editorial Team
Default values appear in create forms only. Edit forms show the persisted entry data.
Help Text
Add instructions below a field using the instructions property within options:
- handle: slug
field:
type: slug
display: URL Slug
required: true
options:
placeholder: e.g. my-page-title
The 18 Field Types
Madori supports these built-in field types:
| Type | Stores | Best For |
|---|---|---|
text |
string |
Titles, names, short text |
slug |
string |
URL identifiers |
markdown |
string |
Long-form plain Markdown |
tiptap |
object (JSON) |
Rich text with formatting |
number |
number |
Prices, counts, measurements |
toggle |
boolean |
On/off flags |
select |
string |
Single choice from options |
multiselect |
string[] |
Multiple choices from options |
date |
string (YYYY-MM-DD) |
Dates |
asset |
string or string[] |
Files and images |
entries |
string[] |
Cross-references to entries |
taxonomy |
string[] |
Term assignments |
replicator |
object[] |
Flexible page blocks (explicit sets) |
blocks |
object[] |
Page blocks (auto-discovers is_block fieldsets) |
grid |
object[] |
Tabular repeatable data |
yaml |
string |
Arbitrary structured data |
code |
string |
Code snippets, embeds |
hidden |
any |
Internal metadata |
See Field Types for detailed configuration options per type.
Visibility Conditions
Visibility conditions show or hide fields based on another field's current value. When a field is hidden, its value is excluded from the submission payload.
Configuration
- handle: field_handle
field:
type: text
visibility:
field: <other_field_handle>
operator: <operator>
value: <comparison_value>
Operators
| Operator | Description | Value Required |
|---|---|---|
equals |
Field value exactly matches the comparison value | Yes |
not_equals |
Field value does not match the comparison value | Yes |
contains |
Field value (string) contains the comparison value as a substring | Yes |
empty |
Field value is undefined, null, or empty string | No |
not_empty |
Field value is defined and not empty | No |
Examples
Show a field when a toggle is enabled:
- handle: show_cta
field:
type: toggle
display: Show Call to Action
default: false
- handle: cta_text
field:
type: text
display: CTA Button Text
visibility:
field: show_cta
operator: equals
value: true
- handle: cta_url
field:
type: text
display: CTA Link URL
validate:
- url
visibility:
field: show_cta
operator: equals
value: true
Show a field based on a select value:
- handle: link_type
field:
type: select
display: Link Type
default: internal
options:
options:
- internal
- external
- handle: external_url
field:
type: text
display: External URL
validate:
- url
visibility:
field: link_type
operator: equals
value: external
- handle: entry_reference
field:
type: entries
display: Linked Entry
visibility:
field: link_type
operator: equals
value: internal
Show a field only when another field has content:
- handle: subtitle
field:
type: text
display: Subtitle
- handle: subtitle_style
field:
type: select
display: Subtitle Style
options:
options:
- normal
- italic
- highlighted
visibility:
field: subtitle
operator: not_empty
Payload Behaviour
When a visibility condition evaluates to false:
- The field is hidden from the editor in the Control Panel
- The field's value is excluded from the submission payload
- Validation rules on the hidden field are not enforced
When a visibility condition evaluates to true (or when no condition is set):
- The field is displayed normally
- The field's value is included in the submission payload
- All validation rules are enforced
Validation Rules
Validation rules enforce data constraints on field values. Rules are checked on both client-side (as the editor types) and server-side (on form submission). Invalid data returns structured field-level errors adjacent to the offending field.
Configuration
Validation rules are defined as an array of strings in the validate property:
- handle: email
field:
type: text
display: Email Address
validate:
- required
- email
- max:254
Available Rules
| Rule | Syntax | Description |
|---|---|---|
required |
required |
Field must have a non-empty value |
min |
min:<n> |
Minimum character length (text types) or minimum value (number) |
max |
max:<n> |
Maximum character length (text types) or maximum value (number) |
regex |
regex:<pattern> |
Value must match the regular expression pattern |
url |
url |
Value must be a valid URL |
email |
email |
Value must be a valid email address |
numeric_range |
numeric_range:<min>,<max> |
Number must be within the specified range |
Rule Compatibility
Not all rules apply to all field types. Incompatible rules are silently ignored with a console warning.
| Rule | Compatible Field Types |
|---|---|
required |
All types |
min, max |
text, slug, markdown, tiptap, code, number |
regex, url, email |
text, slug, markdown, tiptap, code |
numeric_range |
number |
Validation Error Messages
Each rule produces a specific error message:
| Rule | Error Message |
|---|---|
required |
"This field is required" |
min:<n> (text) |
"Must be at least {n} characters" |
max:<n> (text) |
"Must be at most {n} characters" |
min:<n> (number) |
"Must be at least {n}" |
max:<n> (number) |
"Must be at most {n}" |
regex:<pattern> |
"Must match pattern {pattern}" |
url |
"Must be a valid URL" |
email |
"Must be a valid email address" |
numeric_range:<min>,<max> |
"Must be at least {min}" / "Must be at most {max}" |
Required vs. Validate
The required field property and the required validation rule both enforce non-empty values. You can use either:
# Using the field property
- handle: title
field:
type: text
required: true
# Using the validation rule
- handle: title
field:
type: text
validate:
- required
Both approaches produce the same result. Using required: true is the simpler convention for most fields.
Usage Examples
Blog Post Blueprint
A typical blog post with content, metadata, and SEO fields organised into tabs:
tabs:
content:
display: Content
fields:
- handle: title
field:
type: text
display: Title
required: true
validate:
- required
- min:3
- max:120
- handle: slug
field:
type: slug
display: URL Slug
required: true
validate:
- required
- regex:^[a-z0-9-]+$
- handle: featured_image
field:
type: asset
display: Featured Image
options:
max_files: 1
- handle: body
field:
type: tiptap
display: Body
required: true
- handle: categories
field:
type: taxonomy
display: Categories
sidebar:
display: Sidebar
fields:
- handle: status
field:
type: select
display: Status
required: true
default: draft
options:
options:
- draft
- published
- archived
- handle: published_at
field:
type: date
display: Publish Date
- handle: featured
field:
type: toggle
display: Featured
default: false
- handle: author
field:
type: entries
display: Author
sections:
seo:
display: SEO
fields:
- handle: meta_title
field:
type: text
display: Meta Title
validate:
- max:60
options:
placeholder: Override page title for search engines
- handle: meta_description
field:
type: text
display: Meta Description
validate:
- max:160
options:
placeholder: Brief description for search results
Page Builder Blueprint
A flexible page blueprint using Replicator blocks for composing layouts:
tabs:
main:
display: Page
fields:
- handle: title
field:
type: text
display: Title
required: true
- handle: slug
field:
type: slug
display: URL Slug
required: true
- handle: blocks
field:
type: replicator
display: Page Blocks
options:
sets:
- hero
- features_grid
- rich_text
- basic_cta
- testimonials
settings:
display: Settings
fields:
- handle: template
field:
type: select
display: Page Template
default: default
options:
options:
- default
- full-width
- sidebar
- handle: show_navigation
field:
type: toggle
display: Show Navigation
default: true
- handle: custom_css
field:
type: code
display: Custom CSS
visibility:
field: template
operator: equals
value: full-width
Product Blueprint with Conditional Fields
A product schema that reveals different fields based on the product type:
tabs:
details:
display: Product Details
fields:
- handle: name
field:
type: text
display: Product Name
required: true
validate:
- required
- max:200
- handle: slug
field:
type: slug
required: true
- handle: product_type
field:
type: select
display: Product Type
required: true
default: physical
options:
options:
- physical
- digital
- subscription
- handle: price
field:
type: number
display: Price
required: true
validate:
- required
- numeric_range:0,999999
options:
min: 0
step: 0.01
- handle: weight
field:
type: number
display: Weight (kg)
visibility:
field: product_type
operator: equals
value: physical
options:
min: 0
step: 0.1
- handle: download_url
field:
type: text
display: Download URL
validate:
- url
visibility:
field: product_type
operator: equals
value: digital
- handle: billing_interval
field:
type: select
display: Billing Interval
default: monthly
options:
options:
- monthly
- yearly
visibility:
field: product_type
operator: equals
value: subscription
- handle: description
field:
type: tiptap
display: Description
- handle: images
field:
type: asset
display: Product Images
options:
max_files: 10
Form Blueprint
A contact form with honeypot protection:
tabs:
fields:
display: Form Fields
fields:
- handle: name
field:
type: text
display: Full Name
required: true
validate:
- required
- min:2
- max:100
- handle: email
field:
type: text
display: Email Address
required: true
validate:
- required
- email
- handle: subject
field:
type: select
display: Subject
required: true
options:
options:
- General Inquiry
- Support
- Feedback
- Partnership
- handle: message
field:
type: markdown
display: Message
required: true
validate:
- required
- min:10
- max:5000
- handle: website
field:
type: hidden
Common Patterns
Tab Organisation Strategy
Organise tabs by editing frequency. Primary content goes in the first tab. Secondary concerns (SEO, settings, advanced options) go in subsequent tabs:
tabs:
content: # Main content (title, body, media)
sidebar: # Status, dates, taxonomy
seo: # Meta titles and descriptions
advanced: # Developer-facing options
Reusable SEO Section
Add consistent SEO fields across blueprints by defining a section:
sections:
seo:
display: SEO
fields:
- handle: meta_title
field:
type: text
display: Meta Title
validate:
- max:60
options:
placeholder: Override page title for search engines
- handle: meta_description
field:
type: text
display: Meta Description
validate:
- max:160
options:
placeholder: Brief description for search results
- handle: og_image
field:
type: asset
display: Social Share Image
options:
max_files: 1
Or use Fieldsets to share field groups across blueprints.
Progressive Disclosure with Visibility
Use toggles to reveal advanced options, keeping the default editing experience clean:
- handle: show_advanced
field:
type: toggle
display: Show Advanced Options
default: false
- handle: custom_class
field:
type: text
display: CSS Class
visibility:
field: show_advanced
operator: equals
value: true
- handle: custom_id
field:
type: text
display: HTML ID
visibility:
field: show_advanced
operator: equals
value: true
Combining Required + Custom Validation
Layer validation rules for thorough input checking:
- handle: website
field:
type: text
display: Website URL
required: true
validate:
- required
- url
- max:500
- handle: phone
field:
type: text
display: Phone Number
validate:
- regex:^\+?[\d\s\-()]+$
- min:7
- max:20
Status and Publishing Workflow
A common pattern for collection entries with a publish workflow:
- handle: status
field:
type: select
display: Status
required: true
default: draft
options:
options:
- draft
- review
- published
- archived
- handle: published_at
field:
type: date
display: Publish Date
visibility:
field: status
operator: equals
value: published
- handle: archived_reason
field:
type: text
display: Archive Reason
visibility:
field: status
operator: equals
value: archived
Managing Blueprints
Control Panel
Navigate to Blueprints in the CP sidebar to manage blueprints visually:
- Create new blueprints for any entity type
- Add, remove, and reorder fields with drag-and-drop
- Configure field options, validation, and visibility
- Add and organise tabs
API
GET /api/blueprints/{type} # List all blueprints of a type
GET /api/blueprints/{type}/{handle} # Get a specific blueprint
PUT /api/blueprints/{type}/{handle} # Create or update a blueprint
DELETE /api/blueprints/{type}/{handle} # Delete a blueprint
File-Based Management
Edit blueprint YAML files directly for version control and collaboration:
resources/blueprints/
├── collections/
│ ├── blog.yaml
│ ├── pages.yaml
│ └── products.yaml
├── globals/
│ └── site-settings.yaml
├── taxonomies/
│ └── categories.yaml
└── forms/
└── contact.yaml
Changes to blueprint files are reflected immediately in the Control Panel on the next page load.