settings_schema.json specification
Every key in the schema, the setting types and their value rules, and the stored settings document.
On this page
This page is the reference for anyone writing a theme's settings schema. The schema is a required JSON file validated at push against allowlisted types and hard limits; merchant settings are then validated against it on every save and at publish.
Top level#
| Key | Type | Rule |
|---|---|---|
schema_version |
number | must be 1 |
groups |
array | global setting groups; up to 30 |
sections |
array | section types; up to 40 |
No extra keys are accepted at any level (strict). A minimal example:
{
"schema_version": 1,
"groups": [
{
"id": "colors",
"label": "الألوان",
"settings": [
{
"id": "primary",
"type": "color",
"label": "اللون الأساسي",
"default": "#0e7b5c"
}
]
}
],
"sections": []
}Groups#
| Key | Rule |
|---|---|
id |
[a-z0-9_]+, up to 64 characters, unique among groups |
label |
1–200 characters, shown to the merchant |
settings |
up to 60 settings; ids are unique across all groups because they become flat keys in theme_settings |
The ids pages, translations, __proto__, constructor and prototype are reserved and refused as global setting ids.
Setting definition#
| Key | Rule |
|---|---|
id |
[a-z0-9_]+, up to 64 characters |
type |
one of the types |
label |
1–200 characters |
info |
optional, up to 200 characters, shown under the field |
default |
string (up to 5,000 characters), number, boolean or null; must match the type |
options |
select only; 1–50 options { value, label, font? } |
min, max, step |
number and range; min ≤ max and step > 0 |
source |
product and category; true makes the setting a product source for the section |
localized |
text, textarea and richtext; allows an English override in the translations table |
Types#
| Type | Accepted value | Notes |
|---|---|---|
text |
string up to 5,000 characters | |
textarea |
string up to 5,000 characters | |
richtext |
string sanitized at save and at render to p, br, strong, em, u, s, ul, ol, li, h2, h3, blockquote, a |
output only through the richtext filter |
number |
finite number within min/max |
|
range |
number within min/max |
rendered as a slider |
checkbox |
true/false |
|
select |
one of options[].value |
optional font on an option previews the typeface in the editor only |
color |
exactly #rrggbb |
no rgb(), no names |
url |
https://… or a same-host absolute path /… |
// and /\ are rejected |
image |
a stored file reference from the media library | turned into a URL with asset_url/img_url |
video |
a stored file reference | like image |
tel |
a phone number: digits with an optional + |
links only through tel_href/wa_href |
youtube_id |
an 11-character video id | the theme builds a link or thumbnail, never an <iframe> |
product |
a product id in the same store | with source: true its products are fetched |
category |
a category id in the same store | with source: true its products are fetched |
datetime |
an ISO-8601 instant | unparseable values are dropped silently; "" is an accepted default |
Value error strings (such as "x" must be a #rrggbb color) are what the merchant sees in the editor; the list of schema errors at push is in Theme validation.
Section definition#
| Key | Rule |
|---|---|
type |
[a-z0-9_]+, unique among sections |
label |
1–200 characters |
info |
optional; one-line description in the section library |
category |
optional; library grouping label (for example "Banners") |
required |
optional; the merchant cannot remove it and it is auto-injected with its defaults |
band |
optional; a full-bleed strip excluded from alternation |
enabled_on |
optional; 1–4 of home, product, category, cart without repeats; absent = home only |
settings |
up to 60 settings |
blocks |
up to 20 block types { type, label, settings } with unique types inside the section |
An example declaring two sections:
{
"schema_version": 1,
"groups": [
{
"id": "look",
"label": "المظهر",
"settings": [
{
"id": "arabic_numerals",
"type": "checkbox",
"label": "أرقام مشرقية في الأسعار",
"default": false
}
]
}
],
"sections": [
{
"type": "hero_banner",
"label": "بانر رئيسي",
"info": "صورة عريضة مع عنوان وزر",
"category": "بانرات",
"band": true,
"enabled_on": [
"home",
"category"
],
"settings": [
{
"id": "heading",
"type": "text",
"label": "العنوان",
"default": "",
"localized": true
},
{
"id": "image",
"type": "image",
"label": "الصورة"
},
{
"id": "link",
"type": "url",
"label": "الرابط",
"default": ""
}
],
"blocks": []
},
{
"type": "featured_products",
"label": "منتجات مختارة",
"settings": [
{
"id": "title",
"type": "text",
"label": "العنوان",
"default": "",
"localized": true
},
{
"id": "source_category",
"type": "category",
"label": "التصنيف",
"source": true,
"default": ""
},
{
"id": "count",
"type": "range",
"label": "عدد المنتجات",
"min": 4,
"max": 12,
"step": 4,
"default": 8
}
],
"blocks": []
}
]
}The settings document#
This is what is stored per install and reaches the theme as theme_settings (flat globals) and page_sections (resolved instances):
{
"primary": "#0e7b5c",
"arabic_numerals": false,
"pages": {
"home": {
"sections": {
"hero_1": {
"type": "hero_banner",
"settings": {
"heading": "تخفيضات الصيف",
"image": "files/hero.jpg",
"link": "/stores/demo/c/summer"
}
},
"rail_1": {
"type": "featured_products",
"hidden": false,
"settings": {
"title": "الأكثر مبيعاً",
"source_category": "",
"count": 8
}
}
},
"order": [
"hero_1",
"rail_1"
]
}
},
"translations": {
"en": {
"hero_1:heading": "Summer sale",
"rail_1:title": "Best sellers"
}
}
}- Globals are flat top-level keys; that is why
pagesandtranslationsare reserved. pages.<page>.sectionsmaps instance id to{ type, hidden?, settings, blocks?, block_order? }, andpages.<page>.orderorders the ids. Up to 30 instances per page.translations.enis a table of English overrides forlocalizedsettings keyedglobal:<id>,<sectionId>:<key>or<sectionId>:<blockId>:<key>; up to 300 entries. Arabic stays the stored primary and unresolvable keys drop silently.- Conflict rules: an instance whose type the schema no longer declares is dropped silently; an invalid value for a declared type is a hard error; an
orderthat references an unknown id or repeats one is an error;requiredsections are injected when absent.
Additive changes only#
The schema contract is additive: a theme update may add settings (they take their defaults) or sections, and may narrow enabled_on or remove a type (its instances drop). Never change an existing setting's type; give it a new id instead.