Configuration Files Reference
Unfold CMS templates use JSON configuration files to declare metadata, settings UI, page allow-lists, and section schemas. This page documents the complete schema for each file.
template.json
Required. Defines the template's identity, menu locations, and ad zones. Located at the template root directory.
Example
{
"name": "Default Template",
"version": "1.0.0",
"author": "Unfold CMS",
"description": "The default template for Unfold CMS",
"screenshot": "screenshot.png",
"menu_locations": {
"header": {
"name": "Header Navigation",
"description": "Main navigation shown in the site header"
},
"footer": {
"name": "Footer Navigation",
"description": "Footer columns — top-level items become column headers, children become links"
}
},
"zones": {
"header_leaderboard": {
"name": "Header Leaderboard",
"description": "Top of the page leaderboard banner",
"size": "728x90",
"type": "banner"
},
"sidebar_top": {
"name": "Sidebar Top",
"size": "300x250",
"type": "sidebar"
}
}
}
Fields
| Field |
Type |
Required |
Description |
name |
string |
Yes |
Human-readable template name |
version |
string |
No |
Semantic version string |
author |
string |
No |
Template author name |
description |
string |
No |
Short description |
screenshot |
string |
No |
Preview image filename (relative to template root) |
menu_locations |
object |
No |
Menu slots the template supports. Falls back to default header and footer if omitted. |
zones |
object |
Yes |
Ad zone definitions. Use {} if no ad zones are needed. |
| Field |
Type |
Description |
name |
string |
Display name in admin |
description |
string |
Help text for admins |
Zone Object
| Field |
Type |
Description |
name |
string |
Display name in admin |
description |
string |
Help text |
size |
string |
Dimensions (e.g., 728x90, 300x250) |
type |
string |
Zone type: banner, sidebar, popup, footer |
options.json
Optional. Defines the template settings UI rendered in Admin > Appearance > Template Settings. Located at config/options.json within the template directory.
Example
{
"options": [
{
"type": "divider",
"label": "Homepage"
},
{
"key": "hero.enabled",
"type": "boolean",
"label": "Show Hero Section",
"description": "Display the hero banner on the homepage",
"defaultValue": true,
"col": 2,
"children": [
{
"key": "hero.title",
"type": "text",
"label": "Hero Title",
"defaultValue": "Welcome to Our Blog"
},
{
"key": "hero.subtitle",
"type": "textarea",
"label": "Hero Subtitle",
"defaultValue": "Discover stories and expertise.",
"rows": 3
}
]
},
{
"key": "blog.posts_per_page",
"type": "integer",
"label": "Posts Per Page",
"defaultValue": 12,
"min": 4,
"max": 48
},
{
"key": "appearance.default_theme",
"type": "radio",
"label": "Default Theme",
"options": [
{ "value": "system", "label": "System" },
{ "value": "light", "label": "Light" },
{ "value": "dark", "label": "Dark" }
],
"defaultValue": "system",
"col": 2
}
]
}
Field Types
| Type |
Description |
Extra Properties |
text |
Single-line text input |
placeholder |
textarea |
Multi-line text input |
placeholder, rows |
integer |
Number input |
min, max |
boolean |
Toggle switch |
children, bordered |
select |
Dropdown |
options: [{value, label}] |
radio |
Radio button group |
options: [{value, label, description}], inline |
color |
Color picker |
— |
url |
URL input |
placeholder |
file |
File upload |
file: {accept, maxSize, preview} |
slider |
Range slider |
slider: {min, max, step, unit} |
divider |
Section heading (no key) |
label, action: {label, href} |
Common Properties
| Property |
Type |
Description |
key |
string |
Setting key (stored as template.{name}.{key}) |
type |
string |
Field type (see table above) |
label |
string |
Display label |
description |
string |
Help text shown below the field |
defaultValue |
mixed |
Default value |
col |
number |
Columns to span (1 or 2) |
bordered |
boolean |
Show border around field (default: true) |
Boolean Children
Boolean fields can have children — nested fields that appear when the toggle is enabled:
{
"key": "sidebar.enabled",
"type": "boolean",
"label": "Show Sidebar",
"defaultValue": true,
"children": [
{
"key": "sidebar.position",
"type": "radio",
"label": "Position",
"options": [
{ "value": "left", "label": "Left" },
{ "value": "right", "label": "Right" }
],
"defaultValue": "right"
}
]
}
Reading Options in Templates
{{-- Escaped output --}}
@option('hero.title')
{{-- With default --}}
@templateOption('blog.posts_per_page', 12)
{{-- In PHP logic --}}
@php
$postsPerPage = template_option('blog.posts_per_page', 12);
@endphp
pages.json
Optional. Declares which pages the template exposes in the page builder, and which section types each page allows. Located at config/pages.json within the template directory.
Example
{
"homepage": {
"label": "Homepage",
"description": "Sections displayed on your site's homepage",
"url": "/",
"allow": ["hero", "logos", "stats", "features", "testimonials", "faq", "cta"]
},
"contact": {
"label": "Contact Page",
"description": "Sections displayed on the contact page",
"url": "/contact",
"allow": ["contact_info", "contact_form", "contact_faq", "contact_map"]
},
"site": {
"label": "Site-wide",
"description": "Sections shown on every page.",
"url": null,
"allow": ["header", "footer"]
}
}
Page Properties
| Field |
Type |
Required |
Description |
label |
string |
Yes |
Human-readable page name shown in admin |
description |
string |
No |
Help text in the page builder |
url |
string|null |
Yes |
Page URL for the preview link; null for site-wide pages |
allow |
array |
Yes |
Section type IDs (folder names) permitted on this page |
sections/{id}/section.json
Required for each section type. Defines the schema for one section type. Located inside the section folder: sections/{id}/section.json.
Example
{
"name": "Features",
"description": "Feature cards grid with icons and optional links",
"block_type": "features",
"icon": "Zap",
"max": 3,
"reusable": true,
"settings_only": false,
"supports_featured_image": false,
"version": 1,
"fields": [
{ "key": "icon", "type": "icon_picker", "label": "Icon", "placeholder": "Search icons..." },
{ "key": "link_url", "type": "url", "label": "Link URL" }
],
"settings": [
{ "key": "badge", "type": "text", "label": "Badge" },
{ "key": "title", "type": "text", "label": "Title", "col": 2 },
{ "key": "subtitle", "type": "text", "label": "Subtitle", "col": 2 }
]
}
Properties
| Field |
Type |
Required |
Description |
name |
string |
Yes |
Display name in admin |
description |
string |
No |
Help text for admins |
block_type |
string |
Yes |
Internal type identifier (should match the folder name) |
icon |
string |
No |
Lucide icon name for the admin section card |
max |
integer |
No |
Maximum placements of this type per page |
reusable |
boolean |
No |
Whether the same type can be placed more than once |
settings_only |
boolean |
No |
No items — settings form only |
supports_featured_image |
boolean |
No |
Allow image upload per item |
version |
integer |
No |
Schema version (increment when breaking changes are made) |
fields |
array |
No |
Per-item custom fields |
settings |
array |
No |
Section-level settings |
Field / Setting Properties
| Property |
Type |
Description |
key |
string |
Field identifier |
type |
string |
Input type (see table below) |
label |
string |
Display label |
description |
string |
Help text below the field |
placeholder |
string |
Input placeholder |
defaultValue |
mixed |
Default value |
col |
number |
Columns to span (1 or 2) |
options |
array |
For select type: [{value, label}] |
validation |
object |
Validation rules (e.g., {required: true}) |
children |
array |
Nested fields shown when a boolean toggle is on |
file |
object |
For file type: {accept, maxSize, preview} |
slider |
object |
For slider type: {min, max, step, unit} |
rows |
integer |
For textarea type: number of rows |
Field Types
| Type |
Description |
text |
Single-line text input |
textarea |
Multi-line text input |
url |
URL input with validation |
email |
Email input |
integer |
Number input |
boolean |
Toggle switch (supports children) |
select |
Dropdown with options |
radio |
Radio buttons with options |
color |
Color picker |
file |
File / image upload |
slider |
Range slider |
icon_picker |
Lucide icon search and select |
Boolean Children
Boolean fields can contain nested children that appear when the toggle is on:
{
"key": "button_primary",
"type": "boolean",
"label": "Primary Button",
"defaultValue": true,
"children": [
{ "key": "button_primary_label", "type": "text", "label": "Label", "defaultValue": "Get started" },
{ "key": "button_primary_url", "type": "url", "label": "Link", "defaultValue": "/register" }
]
}
Children are stored as flat keys alongside their parent — not nested objects.
File Locations
| File |
Path |
Purpose |
template.json |
resources/views/templates/{name}/template.json |
Template identity |
options.json |
resources/views/templates/{name}/config/options.json |
Admin settings UI |
pages.json |
resources/views/templates/{name}/config/pages.json |
Page definitions + section allow-lists |
section.json |
resources/views/templates/{name}/sections/{id}/section.json |
Section type schema |
seed.json |
resources/views/templates/{name}/sections/{id}/seed.json |
Demo content for installer |