We are introducing a new type: objects to the supported types for theme settings which can be used to replace the existing json_schema type which we intend to deprecate soon.
Defining an objects type theme setting
To create an objects type theme setting, first define a top level key just like any theme setting which will be used as the setting’s name.
links: ...
Next add the type, default and schema keywords to the setting.
links:
type: objects
default: []
schema: ...
type: objects indicates that this will be an objects type setting while the default: [] annotation sets the default value of the setting to an empty array. Note that the default value can also be set to an array of objects which we will demonstrate once the schema has been defined.
To define the schema, first define the name of the schema like so:
links:
type: objects
default: []
schema:
name: link
Next, we will add the properties keyword to the schema which will allow us to define and validate how each object should look like.
links:
type: objects
default: []
schema:
name: link
properties:
name: ...
In the example above, we are stating that the link object has a name property. To define the type of data that is expected, each property needs to define the type keyword.
links:
type: objects
default: []
schema:
name: link
properties:
name:
type: string
The above schema definition states that the link object has a name property of type string which means that only string values will be accepted for the property. Currently the following types are supported:
-
string: Value of property is stored as a string. Settextarea: trueto use an expanding text area:description: type: string textarea: true validations: max_length: 500 -
integer: Value of property is stored as an integer. -
float: Value of property is stored as a float. -
boolean: Value of property istrueorfalse. -
upload: Value of property is the attachment URL -
enum: Value of property must be one of the values defined in thechoiceskeyword.links: type: objects default: [] schema: name: link properties: name: type: enum choices: - name 1 - name 2 - name 3 -
categories: Value of property is an array of valid category ids. -
groups: Value of property is an array of valid group ids. -
tags: Value of property is an array of valid tag names. -
icon: Value of property is the name of a single icon from the Discourse icon set. Selected icons are automatically added to the sprite sheet, so they can be rendered without being registered separately.
When updating an objects theme setting through the API, tags arrays can contain source tag names, integer tag IDs, or both. IDs are resolved to source names before validation and storage. Themes still receive arrays of source names. Numeric strings are treated as names, not IDs, and defaults must use names.
The default frontend tag renderer includes data-tag-id for tag objects with an ID, or for tag names rendered with the tagId option. The existing data-tag-name attribute is unchanged. The ID stays the same across locales even when the displayed name changes. Name-only rendering calls without an ID omit data-tag-id.
Objects with direct tags properties automatically receive an accompanying tag_objects array in the frontend settings:
{
tags: ["support"],
tag_objects: [
{ id: 42, name: "support" }
]
}
The existing tag-name arrays are unchanged. Each companion object contains the tag ID and the exact original source-name string from the setting, including its casing. It does not substitute a translated display name. Components can use the IDs to reference the selected tags, for example when targeting data-tag-id in CSS. Core does not render the companion array as UI or add an admin setting for it.
The companion combines the object’s direct tags selections in their original order, preserving repeated selections. Nested objects get their own companions. Only tags visible to the visitor are included; missing, deleted or hidden selections are omitted. The companion is still present as [] when no tags resolve. Do not assume its indexes match the original string array.
No opt-in or extra browser API request is needed. An author-defined schema property named tag_objects takes precedence: core preserves that field and does not generate a companion at that level. Declare any existing custom field with this name; undeclared tag_objects values are treated as generated metadata and stripped. Objects without direct tags properties and legacy top-level tag-list settings are unchanged.
The companion is generated only for the frontend page data. Stored settings and admin editing values keep their existing shape; generated metadata submitted back through the settings API is discarded. Components supporting contexts or older Discourse versions without this data can check Array.isArray(style.tag_objects) and fall back to their existing name-based behavior when it is unavailable.
With the schema defined, the default value of the setting can now be set by defining a array in yaml like so:
links:
type: objects
default:
- name: link 1
title: link 1 title
- name: link 2
title: link 2 title
schema:
name: link
properties:
name:
type: string
title:
type: string
Required properties
All properties defined are optional by default. To mark a property as required, simply annotate the property with required: true. A property can also be marked as optional by annotating the property with required: false.
links:
type: objects
default: []
schema:
name: link
properties:
name:
type: string
required: true
title:
type: string
required: false
Blank string, datetime and icon values and empty categories, groups and tags lists count as missing: required properties reject them and optional properties skip their validations. false counts as set.
Custom Validations
For certain property types, there are built in support for custom validations which can be declared by annotating the property with the validations keyword.
links:
type: objects
default: []
schema:
name: link
properties:
name:
type: string
required: true
validations:
min_length: 1
max_length: 2048
url: true
Validations for string types
min_length: Minimum length of the property. Value of the keyword has to be an integer.max_length: Maximum length of the property Value of the keyword has to be an integer.url: Validates that the property is a valid URL. Value of the keyword can betrue/false.
Validations for integer and float types
min: Minimum value of the property. Value of the keyword has to be an integer.max: Maximum value of the property. Value of the keyword has to be an integer.
Validations for tags, groups and categories types
min: Minimum number of records for the property. Value of the keyword has to be an integer.max: Maximum number of records for the property. Value of the keyword has to be an integer.
Resolving group membership
Object settings can resolve type: groups properties to a boolean for the current user. This is useful when theme code only needs to know whether the current user is in one of the configured groups, because currentUser.groups only includes groups that are visible to the user.
Add resolve_group_membership: true to the groups property:
menu_sections:
type: objects
default:
- name: section 1
groups:
- 1
- 3
schema:
name: menu section
properties:
name:
type: string
groups:
type: groups
resolve_group_membership: true
The admin UI and stored setting value still use the original groups array. In the frontend runtime settings object, Discourse removes the group IDs from each object and adds a boolean with the same property name prefixed by user_in_:
for (const section of settings.menu_sections) {
if (section.user_in_groups) {
// User is in at least one selected group for this section.
}
}
This option is only valid on object schema properties with type: groups. It also works on nested object schemas and with automatic groups such as logged_in_users and anonymous_users.
Nested objects structure
An object can also have a property which contains an array of objects. In order to create a nested objects structure, a property can also be annotated with type: objects and the associated schema definition.
sections:
type: objects
default:
- name: section 1
links:
- name: link 1
url: /some/url
- name: link 2
url: /some/other/url
schema:
name: section
properties:
name:
type: string
required: true
links:
type: objects
schema:
name: link
properties:
name:
type: string
url:
type: string
Setting description and localization
To add a description for the setting in the en locale, create a file locales/en.yml with the following format given the following objects type theme setting.
sections:
type: objects
default:
- name: section 1
links:
- name: link 1
url: /some/url
- name: link 2
url: /some/other/url
schema:
name: section
properties:
name:
type: string
required: true
links:
type: objects
schema:
name: link
properties:
name:
type: string
url:
type: string
en:
theme_metadata:
settings:
sections:
description: This is a description for the sections theme setting
schema:
properties:
name:
label: Name
description: The description for the property
links:
name:
label: Name
description: The description for the property
url:
label: URL
description: The description for the property
This document is version controlled - suggest changes on github.



