Skip to content

settings_schema.json specification

Every key in the schema, the setting types and their value rules, and the stored settings document.

Updated 2 Sept 20263 min read
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:

settings-schema-minimal.jsonJSON
{
  "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:

settings-schema-sections.jsonJSON
{
  "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):

settings-document.jsonJSON
{
  "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 pages and translations are reserved.
  • pages.<page>.sections maps instance id to { type, hidden?, settings, blocks?, block_order? }, and pages.<page>.order orders the ids. Up to 30 instances per page.
  • translations.en is a table of English overrides for localized settings keyed global:<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 order that references an unknown id or repeats one is an error; required sections 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.