> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sellauth.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Theme Settings and Schema

> How schema.json defines a theme's configurable settings and how settings.json reaches your templates.

## The two files

Every theme has two JSON files at its root, and they do different jobs.

| File | Role |
| - | - |
| `schema.json` | **Definition.** Declares which settings exist, their types, labels, and defaults, and which components can be placed on which pages. It generates the visual editor UI. It is never passed to your templates. |
| `settings.json` | **Values.** Holds the merchant's actual configuration and the component composition of every page. This is the file your templates read. |

You edit `schema.json` by hand when you add a setting. `settings.json` is normally written by the visual editor, though you can edit it directly to change a theme's shipped defaults.

## schema.json

Two top-level keys:

```json theme={null}
{
  "global": {
    "properties": { }
  },
  "components": { }
}
```

`global.properties` are theme-wide settings, available everywhere as `global.properties.<id>`. `components` declares each component type.

### Property definition

```json theme={null}
"accent_color": {
  "label": "Accent Color",
  "type": "color",
  "default": "#2B5FE3",
  "help": "Brand color used for buttons, links and highlights in both schemes."
}
```

| Key | Applies to | Meaning |
| - | - | - |
| `label` | all | Field label in the editor. |
| `type` | all | One of the types below. |
| `default` | all | Value used when the merchant has not set one. |
| `help` | all | Help text shown under the field. |
| `options` | `select`, `radio` | Either plain strings, or `{ "value": ..., "label": ... }` objects. |
| `datalist` | `text`, `aspect_ratio` | Suggested values, still free-text. |
| `maxItems` | `list` | Maximum number of rows. |
| `maxImages` | `image` | Maximum number of images. Defaults to 1. |
| `language` | `code` | Syntax highlighting mode, for example `html`. |
| `properties` | `list` | The per-row fields, defined the same way. |

### Property types

| Type | Editor control | What your template receives |
| - | - | - |
| `text` | Single-line input | String |
| `textarea` | Multi-line input | String |
| `number` | Number input | Number |
| `toggle` | Checkbox card | Boolean |
| `select` | Dropdown | String, one of `options` |
| `radio` | Radio group | String, one of `options` |
| `color` | Color picker | Hex string, for example `#2B5FE3` |
| `font` | Google font picker | Font family name, for example `Geist` |
| `icon` | Font Awesome picker | Icon class string, for example `fab fa-discord` |
| `image` | Image picker | **Array of image IDs.** Pass it through `imageUrl` |
| `list` | Repeatable rows | Array of objects shaped by the nested `properties` |
| `code` | Code editor | String, rendered as-is |
| `aspect_ratio` | Ratio input with suggestions | String, for example `16/9` |
| `products_and_groups` | Product and group picker | Array of `{ "id": ..., "type": "product" \| "group" }` |

A `list` with nested properties:

```json theme={null}
"announcements": {
  "label": "Announcements",
  "type": "list",
  "maxItems": 6,
  "default": [],
  "properties": {
    "text":    { "label": "Text", "type": "text", "default": "New announcement" },
    "link":    { "label": "Link", "type": "text" },
    "new_tab": { "label": "Open in New Tab", "type": "toggle", "default": false }
  }
}
```

```njk theme={null}
{% for announcement in properties.announcements %}
  <a href="{{ announcement.link | shopUrl }}" {{ 'target="_blank"' if announcement.new_tab }}>
    {{ announcement.text | renderString }}
  </a>
{% endfor %}
```

### Component definition

```json theme={null}
"hero": {
  "label": "Hero",
  "multiple": true,
  "templates": ["shop", "custom-page"],
  "properties": { }
}
```

| Key | Meaning |
| - | - |
| `label` | Name shown in the editor. |
| `properties` | The component's settings, using the definitions above. |
| `multiple` | `true` if it can be added more than once per page. |
| `templates` | Allow-list of template keys it can be added to. Omit to allow all. |
| `global` | `true` for chrome that is rendered by the layout on every page, like the navbar. Global components cannot be added or removed, only configured. |
| `order` | Sort position in the editor sidebar, for global components. |
| `page` | `true` for the fixed body of a built-in page, like `page-product` on the `product` template. |

For reference, how Canvas declares its global components:

```json theme={null}
"navbar":       { "label": "Navbar",                "global": true, "order": 1 },
"announcement": { "label": "Announcement Bar",      "global": true, "order": 0 },
"footer":       { "label": "Footer",                "global": true, "order": -1 },
"coupon":       { "label": "Coupon Popup",          "global": true, "order": -2 },
"social-proof": { "label": "Purchase Notifications","global": true, "order": -3 }
```

<Note>
  There is no conditional-visibility key. A field cannot be hidden based on another field's value. Handle dependent options in the component template instead, which is what the official themes do:

  ```njk theme={null}
  {% if properties.variant == 'background-image' %}
    <img src="{{ properties.background_image | imageUrl }}" alt="" />
  {% endif %}
  ```
</Note>

## settings.json

```json theme={null}
{
  "global": {
    "properties": { "accent_color": "#2B5FE3" },
    "components": { "navbar": { "sticky": true }, "footer": { } }
  },
  "templates": {
    "shop": {
      "layout": "master",
      "components": {
        "hero": { "type": "hero", "properties": { "title": "Welcome" } }
      },
      "components_order": ["hero", "products", "faq"]
    }
  }
}
```

How each part reaches your templates:

| In `settings.json` | In your template |
| - | - |
| `global.properties.accent_color` | `global.properties.accent_color`, available everywhere |
| `templates.<key>.layout` | Selects `layouts/<layout>.njk` |
| `templates.<key>.components_order` | `components_order`, the array you loop over |
| `templates.<key>.components.<id>` | Resolved by `{% render_component id %}` |
| `templates.<key>.components.<id>.properties` | `properties` inside that component |
| `templates.<key>.components.<id>.type` | Picks `components/<type>.njk` |

Component instance IDs are arbitrary strings. A component that has `multiple: true` gets one entry per instance, so a page can have three `hero` entries with three different IDs, all rendering `components/hero.njk`.

## Adding a setting

<Steps>
  <Step title="Declare it in schema.json">
    ```json theme={null}
    "show_badge": {
      "label": "Show Badge",
      "type": "toggle",
      "default": true
    }
    ```
  </Step>

  <Step title="Read it with a fallback">
    For a toggle that defaults to `true`, test against `false` so an unset value still counts as on:

    ```njk theme={null}
    {% if properties.show_badge != false %}
      <span class="badge">New</span>
    {% endif %}
    ```

    For a toggle that defaults to `false`, a plain truthiness check is enough. For everything else, use the inline conditional:

    ```njk theme={null}
    {{ properties.heading if properties.heading else 'Featured products' }}
    ```
  </Step>
</Steps>

<Warning>
  Defaults in `schema.json` are **not** backfilled into an existing `settings.json`. A merchant who installed your theme before you added a setting will have no value for it, so the fallback in the template is what they actually get. Always write one.
</Warning>

## Nunjucks inside setting values

Setting values can contain Nunjucks expressions. They are evaluated when the template pipes the value through `renderString`:

```json theme={null}
"copyright": "Copyright {{ shop.name }} {{ '' | currentYear }}"
```

```njk theme={null}
<p>{{ properties.copyright | renderString }}</p>
```

Without `renderString` the value is printed literally, braces and all. Use it for any text setting where a merchant might reasonably want to interpolate their shop name or the current year.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.