Theme settings
Define and access theme-level settings — global brand controls editors configure from the admin
Theme settings let editors configure global controls — brand colours, typography, social links, default behaviours — from the admin's theme configuration page. Defined once in config/settings_schema.json, accessed everywhere via the settings() global.
This page covers both defining and accessing settings. Template-level settings (per-page options) are covered in Templates.
Theme settings vs template settings
| Aspect | Theme settings | Template settings |
|---|---|---|
| Defined in | config/settings_schema.json | {% schema %} inside a template |
| Structure | Groups with a fields array | A settings array |
| Scope | Global — applies across all pages | Per-page — applies only to pages using that template |
| Accessed via | settings() global function | page.theme.settings on the document |
| Use case | Brand colours, fonts, social links | Hero style, sidebar toggle, layout options |
| Edited in admin | Theme configuration page | Page editor, per document |
Use theme settings for values that should stay consistent site-wide. Use template settings for options that vary per page.
Defining theme settings
1. Create the settings schema file
your-theme/
├── blocks/
├── config/
│ └── settings_schema.json
├── layouts/
├── templates/
└── assets/2. Define the groups array
The file is a JSON array of groups. Every group has a name.
The first group is reserved for theme metadata — your theme's name, version, author, and documentation links. It has no type and no fields:
[
{
"name": "theme_settings",
"theme_name": "My Arts Centre",
"theme_version": "1.0.0",
"theme_author": "Your Studio",
"theme_documentation_url": "https://example.com/docs",
"theme_support_url": "https://example.com/support"
}
]Every other group is configurable. A configurable group sets "type": "group" and lists its controls in a fields array. Each field uses default for its starting value:
{
"name": "global",
"type": "group",
"fields": [
{
"type": "text",
"name": "site_title",
"label": "Site title",
"default": "My Arts Centre"
}
]
}3. Add a colour palette group
{
"name": "colours",
"type": "group",
"fields": [
{
"type": "color",
"name": "color_primary",
"label": "Primary colour",
"default": "#1a1a2e"
},
{
"type": "color",
"name": "color_secondary",
"label": "Secondary colour",
"default": "#e94560"
},
{
"type": "color",
"name": "color_background",
"label": "Background colour",
"default": "#ffffff"
},
{
"type": "color",
"name": "color_text",
"label": "Body text colour",
"default": "#333333"
}
]
}4. Add typography and social media groups
{
"name": "typography",
"type": "group",
"fields": [
{
"type": "select",
"name": "heading_font",
"label": "Heading font family",
"options": [
{ "label": "System default", "value": "system-ui" },
{ "label": "Georgia", "value": "Georgia, serif" },
{ "label": "Helvetica", "value": "Helvetica, Arial, sans-serif" }
],
"default": "system-ui"
},
{
"type": "select",
"name": "body_font",
"label": "Body font family",
"options": [
{ "label": "System default", "value": "system-ui" },
{ "label": "Georgia", "value": "Georgia, serif" }
],
"default": "system-ui"
},
{
"type": "number",
"name": "base_font_size",
"label": "Base font size (px)",
"default": 16
}
]
}{
"name": "social",
"type": "group",
"fields": [
{ "type": "url", "name": "facebook_url", "label": "Facebook page URL" },
{ "type": "url", "name": "twitter_url", "label": "X (Twitter) profile URL" },
{ "type": "url", "name": "instagram_url", "label": "Instagram profile URL" },
{ "type": "url", "name": "youtube_url", "label": "YouTube channel URL" }
]
}A group can also be repeatable — set "type": "array" with the same fields shape, and editors can add multiple entries (for example, a list of footer links).
All groups appear in the admin on the theme configuration page.
Accessing settings in templates
Theme-level: settings() global
The settings() function returns an object grouped the same way as the schema — each group's name is a key, and its fields sit beneath it:
{% assign theme_settings = settings() %}
<body style="
--color-primary: {{ theme_settings.colours.color_primary }};
--color-secondary: {{ theme_settings.colours.color_secondary }};
--color-background: {{ theme_settings.colours.color_background }};
--color-text: {{ theme_settings.colours.color_text }};
--font-heading: {{ theme_settings.typography.heading_font }};
--font-body: {{ theme_settings.typography.body_font }};
--font-size-base: {{ theme_settings.typography.base_font_size }}px;
">Or access individual settings inline through their group:
<a href="{{ settings().global.ticketLink }}">Find tickets</a>Template-level: page.theme.settings
Template settings come from the document object — page.theme.settings for a page, event.theme.settings for an event, and so on. The variable matches the template's collection.
{% assign header_style = page.theme.settings.header_style %}
{% assign show_breadcrumbs = page.theme.settings.show_breadcrumbs %}
{% if show_breadcrumbs %}
{% render 'breadcrumbs', page: page %}
{% endif %}
<header class="page__header page__header--{{ header_style }}">
<h1>{{ page.title }}</h1>
</header>Patterns
Inject brand colours as CSS custom properties
In layouts/default.liquid, settings flow into all templates as variables:
<head>
<meta charset="utf-8">
<title>{{ page.title | default: settings().global.site_title }}</title>
{{ content_for_header }}
{{ "theme.css" | asset_url | stylesheet_tag }}
<style>
:root {
--color-primary: {{ settings().colours.color_primary | default: '#1a1a2e' }};
--color-secondary: {{ settings().colours.color_secondary | default: '#e94560' }};
--color-bg: {{ settings().colours.color_background | default: '#ffffff' }};
--color-text: {{ settings().colours.color_text | default: '#333333' }};
--font-heading: {{ settings().typography.heading_font | default: 'system-ui' }};
--font-body: {{ settings().typography.body_font | default: 'system-ui' }};
}
</style>
</head>Per-page hero variant
{% assign hero_variant = page.theme.settings.hero_variant %}
{% assign overlay_opacity = page.theme.settings.overlay_opacity %}
{% if page.image %}
<section class="hero hero--{{ hero_variant | default: 'full-width' }}">
<img
src="{{ page.image | image_url: width: 1600, height: 900, fit: 'cover' }}"
alt="{{ page.image.alt | default: page.title }}"
loading="eager"
>
{% if hero_variant == 'overlay' %}
<div class="hero__overlay" style="opacity: {{ overlay_opacity | default: 0.5 }};">
<h1 class="hero__title">{{ page.title }}</h1>
</div>
{% endif %}
</section>
{% endif %}
<div class="page__body">
{% stageblocks page %}
</div>
{% schema %}
{
"settings": [
{
"type": "select",
"name": "hero_variant",
"label": "Hero style",
"options": [
{ "label": "Full width", "value": "full-width" },
{ "label": "Overlay", "value": "overlay" },
{ "label": "Side by side", "value": "side-by-side" }
],
"defaultValue": "full-width"
},
{
"type": "number",
"name": "overlay_opacity",
"label": "Overlay opacity (0 to 1)",
"defaultValue": 0.5
}
],
"blocks": ["text-section", "image-gallery", "call-to-action"]
}
{% endschema %}Conditional rendering based on settings
{% assign social = settings().social %}
{% if social.facebook_url != blank or social.twitter_url != blank or social.instagram_url != blank %}
<nav class="social-links" aria-label="Social media">
<ul>
{% if social.facebook_url != blank %}
<li><a href="{{ social.facebook_url }}" rel="noopener">Facebook</a></li>
{% endif %}
{% if social.twitter_url != blank %}
<li><a href="{{ social.twitter_url }}" rel="noopener">X (Twitter)</a></li>
{% endif %}
{% if social.instagram_url != blank %}
<li><a href="{{ social.instagram_url }}" rel="noopener">Instagram</a></li>
{% endif %}
{% if social.youtube_url != blank %}
<li><a href="{{ social.youtube_url }}" rel="noopener">YouTube</a></li>
{% endif %}
</ul>
</nav>
{% endif %}Troubleshooting
"Invalid settings_schema.json" — JSON syntax error. Validate with a JSON linter.
"settings_schema.json should be an array of objects" — The root must be [], not {}.
"settings_schema.json entries should include a name property" — Every group needs a name.
Settings return empty values — The setting name in the schema must match the property name used in the template. Names are case-sensitive.
settings() returns an empty object — The theme has no config/settings_schema.json, or the file failed validation. Check the upload validation log.
Template settings not available on page.theme.settings — The template needs a valid {% schema %} with a settings array. Validate the schema JSON.
Related
- Templates — define template-level settings.
- Block schema reference — every field type usable in settings.
- Theme directory structure — where the settings file lives.
- Liquid filters —
asset_url,image_url, and other filters used in settings-driven markup.