Schemas

A schema is a JSON file that defines which data is collected for the schema from the studio and the API hub.

A schema can be for:

  • Frontend components
  • Dynamic pages
  • Page folders
  • Project settings
  • Data sources

This article will go through the general schema structure for all our schemas, schema field types, and how to access the data.

General schema structure

The basic structure of a schema is as follows:

{
"<schema>Type": "<customer>/<project>/identifier",
"name": "Name display in the studio",
"category": "Category type to display in the studio",
"icon": "Icon name that displays in the studio",
"schema": [
{
// ....
}
]
}

You can store the schema.json file wherever you like, because it's only required in the studio and not by the code.

The <schema>Type is the most important piece as it connects the schema to the React component in your project and they must be identical to the mapping defined there.

The name is also important because this is what the user sees in the studio.

The category can be whatever you choose to organize your schemas in the studio.

The icon can be used to define a speaking icon from Material icons.

Schema section
The schema section is an array of configuration sections.

These are displayed in different parts of the studio. For example, for Frontend components, these are displayed in the component editor panel.

{
// ...
"schema": [
{
"name": "First section in the studio",
"fields": [
{
"label": "Label in the studio",
"field": "identifierUsedToAccessFieldInDataProp",
"type": "string"
}
// ...
]
},
{
"name": "Second section in the studio",
"fields": [
{
"label": "Label for field in the studio",
"field": "fieldIdentifierMustBeUnique",
"type": "string"
}
// ...
]
}
]
}

You can move around fields between these sections as you like because the resulting data property will be flat (without the sections). They're only for visualization purposes in the studio. That also means that every field identifier (see below) must be unique across the entire schema. The only exceptions are fields that are part of a group (also see below).

Each schema section has a name (the section headline in the component editor panel) and a number of fields.

Each object in fields consists of a similar structure which can slightly differ depending on the type of the field, for example:

{
"tasticType": "<projectName>/<componentName>",
"name": "<componentName>", // this will be visible in the studio
"icon": "<iconName>", // select from https://fonts.google.com/icons?icon.set=Material+Icons
"category": "<categoryType>", // this should be the category type of your component but you can name it as you wish
"schema": [
{
"name": "Data source", // this will be visible in the studio and you can name this what you like
"fields": [
{
"label": "Custom data source", // this is what's displayed in the studio
"field": "someDataSource", // this is the identifier used to access the field in the data prop
"type": "dataSource", // this must be a type of "dataSource"
"dataSourceType": "<yourCompanyName>/<dataSourceName>", // this must match the customDataSourceType in your data source schema
"default": null
}
]
}
]
}
{
"customDataSourceType": "<yourCompanyName>/<dataSourceName>",
"name": "<New data source>", // this will be displayed in the studio
"category": "Content", // this should be the category type of your data source but you can name it as you wish
"icon": "source", // you can choose any icon from material icons (select from https://fonts.google.com/icons?icon.set=Material+Icons)
"schema": [
{
"name": "Configuration", // this will be visible in the studio and you can name this what you like. You can have as many sections of the schema as you wish
"fields": [
{
//Required fields
"label": "Query", // this is what's displayed in the studio
"field": "query", // this is the identifier used to access the field in the data prop
"type": "text", // this tells the studio what field type to display
//Optional fields
"default": "",
"required": "true", // set to true if the data source is required for your component, the user of the studio will be warned if a data source isn't selected.
"translatable": false
}
]
}
]
}

Each field has a label which is displayed to the user as the prompt to insert some information.

The property field determines how you can access the field in the data prop. Remember that field identifiers must be unique across a schema.

The type determines what kind of information should be input by the user in the studio. For more information, see Schema field types.

The remaining properties of a field are optional and help you to configure the behavior of the field in the studio:

  • translatable: Determines if the field value should be available in different languages (see the internationalization article for more information. The default of this setting depends on the field type (see below). Learn more about using multiple languages for a field on the locales and translations article.
  • required (default: false): If the setting is mandatory in the studio. You'll still need to check if the value is set in your Frontend component code because it might not be loaded yet (asynchronous) or the user in the studio ignored the warning about a mandatory setting (for example, to store a draft).
  • default (default null): Define a custom default value you want to receive when the config is left empty in the studio.
  • values (default null): Array of key-value pairs defining values for enum field type. Providing this field is mandatory when the field type is set to enum.

Schema field types

String

The field of type string is used as a one-line input field. By default, this field is translatable (translatable = true) based on different locales configured for the project.

String example

//...
"fields": [
{
"label": "Headline",
"field": "label",
"type": "string"
},
]
//...

Text

The field of type text is used as a multi-line input field. By default, this field is translatable (translatable = true) based on different locales configured for the project.

Text example

//...
"fields": [
{
"label": "Teaser text",
"field": "teaserText",
"type": "text",
"default": null
},
]
//...

Boolean

The field of type boolean is used as a toggle to return true or false. This field is not translatable.

Boolean example

//...
"fields": [
{
"label": "Full Viewport Width?",
"field": "isClickable",
"type": "boolean",
"default": false
},
]
//...

Markdown

The field of type markdown allows users to write formatted text using Markdown. By default, this field is translatable (translatable = true) based on different locales configured for the project.

For more information on using Markdown, see Using the Markdown editor.

Markdown example

//...
"fields": [
{
"label": "Content",
"field": "text",
"type": "markdown",
"default": "* Enter\n* some\n* Markdown",
"translatable": true
},
]
//...

Number, Integer, or Decimal

The field of type number, integer, or decimal allows users to input a numeric value. This field is not translatable.

Number example

//...
"fields": [
{
"label": "Space in px (Overrides Size field)",
"field": "spaceInPx",
"type": "integer",
"default": ""
}
]
//...

Enum

The field of type enum allows users to select an option from a drop-down. An additional property, values, is used to set the options. It holds an array of objects, each with a name and value pair. The name is displayed on the studio and the value in your Frontend component code. This field is not translatable.

Enum example

// ...
"fields": [
{
"label": "Theme",
"field": "theme",
"type": "enum",
"required": false,
"values": [
{
"value": "dark",
"name": "dark"
},
{
"value": "light",
"name": "light"
}
],
"default": "dark"
},
//...

Image

The field of type media allows users to select an image from the media library and configure additional settings such as an alt text, crop ratio, and where the crop anchor should be in the image. You can influence if the user can select a ratio or pre-define a fixed value. This field is not translatable.

Image example

// ...
{
"name": "Image",
"helpText": "Customize your image",
"fields": [
{
"label": "Image",
"field": "image",
"type": "media",
"required": true
}
]
},
//...

Node

The field of type node allows users to select a page folder from the page folder tree in the site builder and returns the corresponding page folder ID. This field is not translatable.

Node example

//...
{
"label": "Node",
"field": "node",
"type": "node",
"required": false,
"default": null
},
//...

Reference

The field of type reference is used to give users the choice to select any a page folder (like from the node type) or an external link URL. For more information, see Working with links. This field is not translatable.

Reference example

//...
{
"label": "Link",
"field": "reference",
"type": "reference",
"required": false,
"default": null
},
//...

Tree

The field of type tree allows users to select a page folder in the site builder, and you'll receive the entire sub-tree of page folders that starts there. This is useful if you want to render a navigation. This field is not translatable.

Tree example

//...
{
"label": "Navigation tree",
"field": "tree",
"type": "tree"
},
//...

Instant

The field of type instant allows users to select date and time using an input field displaying a calendar and clock. Note that the date and time selected is based on the users' local time. This field is not translatable.

Instant example

{
"label": "Countdown to",
"field": "countdownTo",
"type": "instant",
"default: "Fri Jul 15 2022 16:14:00 GMT+02"
},

Json

The field of type json is meant for copy and paste of complex information (for example, exports from other systems). You can ask users to paste any JSON and you will receive it automatically parsed to an object. This field is not translatable.

JSON example

//...
{
"label": "Teaser JSON",
"field": "teaserJson",
"type": "json",
"default": null
}
//...

Data source

The field of type dataSource is special as you will only receive the result from a data source query and not the user-entered configuration. For example, if you have dataSourceType: product-list, studio users will be able to filter products from the commerce backend by various criteria. In your Frontend component, you'll simply receive the products that are returned by this query.

Every field of type dataSource needs a dataSourceType parameter:

//...
{
"name": "Data source selection",
"fields": [
//...
{
"label": "Data source",
"field": "data source",
"type": "dataSource",
"dataSourceType": "myShop/product-list",
"default": null
}
//...
]
},
//...

It'll display as in the studio as below:

55218cf StreamType

As you're creating a schema for a data source, the possible values for dataSourceType must match what you've inputted in your data source schema.

Group inputs for Frontend components

In some cases, a single input isn't enough. Then you typically don't want to have multiple fields like image1, image2, image3 but use a field of type group to let the user in the studio create a flexible number of group elements as values for the field.

The elements of a group can contain any number of fields again. The basic configuration of a group looks like this:

{
// ...
"schema": [
{
// ...
"fields": [
{
"label": "Top categories",
"field": "topCategories",
"type": "group",
"itemLabelField": "name",
"min": 1,
"max": 5,
"fields": [
{
"label": "Category name",
"field": "name",
"type": "string",
"translatable": false,
"required": true
},
{
"label": "Navigation tree",
"field": "tree",
"type": "tree"
}
//...
},
// ...
]
},
]
}

Always use a label as the first option along with "itemLabelField": "name"so that this is displayed in the component settings panel.

This schema creates a group of category fields. The user in the studio can add a number of name items (at least 1, up to 5, min/max are optional) in the group and as the value, you receive an array of these elements in your Frontend component.

4e3ba09 Groups

Then if you click the edit icon on a country or + Add countries, you'll get pop-up settings:

a4cac57 Group pop up

Help text

You can provide additional information to your users in the studio by using the field type description or image. In contrast to normal fields, this doesn't expect the label and field properties, only a type plus their specific value properties. For example:

{
// ...
"schema": [
{
// ...
"fields": [
{
"type": "description",
"text": "I'll be displayed in the studio as information."
},
{
"type": "image",
"url": "https://commercetools.com/_build/images/logos/commercetools-logo-desktop.svg"
}
// ...
]
}
]
}

You can also use helpText which displays at the top of the section and appears on hover:

//...
{
"name": "Image",
"helpText": "Customize your image",
"fields: [
//...
}
//...

Best practices

When you first create a Frontend component schema only make configurable what really needs to be configured in the studio. In some cases, it means that nothing will be configurable. For example, the checkout Frontend component is only placed on the page and the code takes care of the rest. It's only when your team needs something to be configurable should you add it into your schema.

If you ever add any new configurations to an existing Frontend component, they must be optional and set to a default that will result in the old behavior (for example, false in the example above).

Copy the schema of your latest and most simple Frontend component or a similar Frontend component to what you want to create as a starting point for your new schema. Test it to make sure it works and then tweak it to how you want it. That way you don't have to rewrite a lot of code, you keep the same format and conventions, and you know how it'll behave.

Keep as much business logic outside of the Frontend component itself and put that into the patterns or explicit logic components. Use the Frontend component as a data layer to feed the components. It might even be a good idea to translate the data structure coming from API hub into your own semantic data structure to increase the re-use of your components.

When deciding how to slice up a Frontend component, it's sometimes best to make one big block in the first instance and then refactor only if you need to make smaller parts configurable. Think about how it will be used and how it makes sense from a user point of view. If it makes sense from the get-go to make 2 (or more) smaller Frontend component, then make it like that. But if you're not sure how to split it up, make 1 big one. It'll save you setup time, and you won't have to think about so many possibilities to account for in your code. You can always cut it down in the future. You can even use our grid system within a Frontend component, which will then make it easier when refactoring and will make it all look the same even if you're using 1 big Frontend component versus 4 small Frontend component.

In general, take each Frontend component on a case-by-case basis. In some cases, some of these best practices won't work for the individual Frontend component you're creating as it's unique and how you handle it is unique.

If you want to, you could limit the number of products that will be displayed to your customers in a product slider, or add a title to it, or even a description. You can add all these things and more into the schema of your component and make them configurable by the users of the studio.

Composable Commerce
Getting StartedMerchant CenterTutorialsHTTP APIGraphQL APIImport & ExportSDKs & Client LibrariesCustom Applications
Frontend
Getting StartedStudioDevelopingHTTP API
Sign upLog inSupportStatusOfferingTech BlogIntegrationsUser Research Program
Copyright © 2023 commercetools
Privacy PolicyImprint