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
(defaultnull
): Define a custom default value you want to receive when the config is left empty in the studio.values
(defaultnull
): Array of key-value pairs defining values forenum
field type. Providing this field is mandatory when the field type is set toenum
.
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.
//..."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.
//..."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.
//..."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.
//..."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.
//..."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.
// ..."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.
// ...{"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.
//...{"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.
//...{"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.
//...{"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.
{"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.
//...{"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:
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.
Then if you click the edit icon on a country or + Add countries, you'll get pop-up settings:
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.