Learn how to manage templates, add or remove blocks, and customize your Store Theme.
We call a template your website's page structure.
Templates are responsible for declaring JSON
blocks that, once rendered, will define the set of components for your website's pages, such as the home page, product page, search results page, etc.
As you could see in the previous step, the Store Theme app already implements basic templates for each and every of your store’s pages in its blocks.json
and blocks.jsonc
files. This is what allowed your store to display VTEX Store Framework default components even when no configurations were performed by you in the code.
In practice, managing templates is what allows you to build the desired theme for your website by adding or removing blocks according to your store's needs.
Step 1 - Understanding what is a JSON block
In order to edit templates and consequently your store's components, we need to take a closer look at what blocks are and how they are applied to the Store Theme.
Blocks are the minimal Store Framework abstraction of React components that we want to see on the UI. Blocks are small pieces of code, exported to the platform by independent apps that configure how they will be rendered on your website.
Although they may look simple at first, blocks are imbued with higher flexibility, allowing you to achieve complex scenarios and specific component behaviors by configuring their properties (props) or even declaring them in other blocks.
This means that whenever you edit your theme's code using the Store Theme app, you're also editing blocks that will end up being your store's page components when rendered.
We declare a new block in a template in order to add a new component to a page, in the same way that a component can be excluded from a page simply by removing a block from a template.
Step 2 - Managing blocks in your theme
As we have previously seen, the folder responsible for organizing your store’s blocks and templates is called store
.
In it, you can declare all your blocks in the blocks.jsonc
file or create as many blocks.jsonc
files and folders as you want. You can also declare blocks using the blocks
subfolder. The only difference between the two folders is that jsonc
files allow you to comment in the code.
As previously mentioned, blocks are pieces of code exported by VTEX Store Framework apps. This means that whenever a block is used in your theme, the app behind it must be declared in your Store Theme dependencies list.
Have a look at the manifest.json
file from the Store Theme app. You'll come across an object called dependencies
which contains the names of several apps and their respective versions. These apps are already listed, since the default Store Theme code already declared templates which in turn use blocks that these apps export.
When declaring a new block in your Store Theme app, make sure to check that the app responsible for that block is listed as a dependency. If it's not, open the manifest.json
file and add the app's name and desired version in the dependencies
list, following this format: "vtex.{appName}": "{majorVersion}.x"
.
Step 3 - Understanding a block's structure
Let’s now have a look at your store’s predefined homepage template:
- Open the Store Theme app using any code editor, such as Visual Studio Code.
- Access
store
and thenblocks
. - Acess
home
and thenhome.jsonc
. You will be able to see a result similar to the one below:
_123{_123 "store.home": {_123 "blocks": [_123 "list-context.image-list#demo",_123 /* You can make references to blocks defined in other files._123 * For example, `flex-layout.row#deals` is defined in the `deals.json` file. */_123 "flex-layout.row#deals",_123 "rich-text#shelf-title",_123 "flex-layout.row#shelf",_123 "info-card#home",_123 "rich-text#question",_123 "rich-text#link",_123 "newsletter"_123 ]_123 },_123_123 "shelf#home": {_123 "blocks": ["product-summary.shelf"]_123 },_123_123 "product-summary.shelf": {_123 "children": [_123 "product-summary-name",_123 "product-summary-description",_123 "product-summary-image",_123 "product-summary-price",_123 "product-summary-sku-selector",_123 "product-summary-buy-button"_123 ]_123 },_123_123 "list-context.image-list#demo": {_123 "children": ["slider-layout#demo-images"],_123 "props": {_123 "height": 720,_123 "images": [_123 {_123 "image": "https://storecomponents.vteximg.com.br/arquivos/banner-principal.png",_123 "mobileImage": "https://storecomponents.vteximg.com.br/arquivos/banner-principal-mobile.jpg"_123 },_123 {_123 "image": "https://storecomponents.vteximg.com.br/arquivos/banner.jpg",_123 "mobileImage": "https://storecomponents.vteximg.com.br/arquivos/banner-principal-mobile.jpg"_123 }_123 ]_123 }_123 },_123 "slider-layout#demo-images": {_123 "props": {_123 "itemsPerPage": {_123 "desktop": 1,_123 "tablet": 1,_123 "phone": 1_123 },_123 "infinite": true,_123 "showNavigationArrows": "desktopOnly",_123 "blockClass": "carousel"_123 }_123 },_123_123 "rich-text#shelf-title": {_123 "props": {_123 "text": "## Summer",_123 "blockClass": "shelfTitle"_123 }_123 },_123 "flex-layout.row#shelf": {_123 "children": ["list-context.product-list#demo1"]_123 },_123 "list-context.product-list#demo1": {_123 "blocks": ["product-summary.shelf"],_123 "children": ["slider-layout#demo-products"],_123 "props": {_123 "orderBy": "OrderByTopSaleDESC"_123 }_123 },_123 "slider-layout#demo-products": {_123 "props": {_123 "itemsPerPage": {_123 "desktop": 5,_123 "tablet": 3,_123 "phone": 1_123 },_123 "infinite": true,_123 "fullWidth": true,_123 "blockClass": "shelf"_123 }_123 },_123_123 "info-card#home": {_123 "props": {_123 "id": "info-card-home",_123 "isFullModeStyle": false,_123 "textPosition": "left",_123 "imageUrl": "https://storecomponents.vteximg.com.br/arquivos/banner-infocard2.png",_123 "headline": "Clearance Sale",_123 "callToActionText": "DISCOVER",_123 "callToActionUrl": "/sale/d",_123 "blockClass": "info-card-home",_123 "textAlignment": "center"_123 }_123 },_123_123 "rich-text#question": {_123 "props": {_123 "text": "**This is an example store built using the VTEX platform.\nWant to know more?**",_123 "blockClass": "question"_123 }_123 },_123_123 "rich-text#link": {_123 "props": {_123 "text": "\n**Reach us at**\nwww.vtex.com.br",_123 "blockClass": "link"_123 }_123 },_123_123 "product-summary-buy-button": {_123 "props": { _123 "displayBuyButton": "displayButtonAlways"_123 }_123 }_123}
As we can see, the default store.home
homepage template declares the following blocks:
list-context.image-list#demo
flex-layout.row#deals
rich-text#shelf-title
flex-layout.row#shelf
info-card#home
rich-text#question
rich-text#link
newsletter
This means that your default store homepage will comprise the components defined by these blocks.
The same file (home.jsonc
) also has each block’s declaration as well as their configuration, using the block's props and even other blocks in order to build more complex components.
More than simply declare a block in the block list template, notice that you will also need to declare the block in order to set its behavior when rendered as a component. For this purpose, you will need to use the block's props (as shown in the next section) and other child blocks as well to define its configuration (as shown in the Block composition section).
Step 4 - Clarifying block naming and properties
Still in the home.jsonc
file, use ctrl+f
and look up for the rich-text#question
block:
_10"rich-text#question": {_10 "props": {_10 "text": "**This is an example store built using the VTEX platform. Want to know more?**",_10 "blockClass": "question"_10 }_10},
This block is responsible for rendering a component that displays markdown texts to users.
As we can see, the rich-text#question
block is using two props: text
and blockClass
. These are responsible for defining which text the component will display and for defining an ID that will be used for its customization - as we will see in the next step of this track.
You can now take a look at the documentation of the app behind the block (also called Rich Text) and check the available props table in the Configuration section.
When looking at the documentation, you'll notice that the exported block's name is merely rich-text
, however Store Theme uses rich-text#block
. This is due to the fact that we can use a #
after the block's official name to easily identify when inserting it in our theme's code, thereby better organizing the theme itself.
All the props available to configure a block can be found in the documentation of its exporting app or in the block's own documentation (if it exists).
Step 5 - Understanding blocks composition
Now, let's take a look at the shelf#home
block in the same file (home.jsonc
):
_10"shelf#home": {_10 "blocks": ["product-summary.shelf"]_10},
Note that it declares another block to your blocks
list, that in turn declares other blocks below in a list called children
:
_10"product-summary.shelf": {_10 "children": [_10 "product-summary-name",_10 "product-summary-description",_10 "product-summary-image",_10 "product-summary-price",_10 "product-summary-sku-selector",_10 "product-summary-buy-button"_10 ]_10},
You'll notice that in the example above, product-summary.shelf
requires other blocks as children, such as product-summary-name
, to properly render the component.
As previously mentioned, a component can be a crossroad for several different blocks, and therefore one of your theme's blocks may need to list other blocks to achieve the proper rendering results on the UI.
To build a component using several blocks, the main block can declare a blocks
list onto itself, such as shelf#home
, or it can declare a list of children
blocks, as the product-summary.shelf
block does.
What defines whether a block declares other blocks using a blocks
or a children
list is the composition of the blocks about to be declared in the list.
When a block is about to be developed for the framework, it gets a composition definition that can either be blocks
or children
:
blocks
composition blocks have a fixed position on the store's page irrespective of where they were declared in the code, leading to a preordained position on the UI.children
composition blocks on the other hand do not have a fixed position on the store's page, which means that how they're declared in the code directly impacts the page position. Thechildren
block declared first will be at the top of the page, followed by the second block below it and so on.
The conclusion is that, according to their composition, the listed blocks themselves will define if they should be declared in the parent block's blocks
list or in its children
list when building a single component on the UI.
In our example, the product-summary.shelf
block has a blocks
composition, while product-summary-name
has a children
composition. This explains why they are declared, respectively, in a blocks
and children
lists.
You can find out which composition a block has by looking at its code in the exporting app's
interfaces.json
file.