Grid layout
Renders content on a two-dimensional grid layout.
Node name : grid-layout

Introduction
Horizontal and vertical layout are mostly intended for one-dimensional layouts.
If you need a two-dimensional layout approach you can use a grid layout component.
The first step is to create a grid definition which set columns and rows behavior.
Note
If no grid definition is set, the grid layout will act in the same way as a vertical-layout (only one column and each element displayed on its own row).
The next step is to create a grid content containing the nodes you want to render inside the grid cells. Each node will render inside the next available cell, starting by the top left of the grid, then spreading by rows then by column to end with the bottom right cell of the grid.
It's also possible to make a node span across multiple rows or columns with the associated directives (grid:colspan & grid:rowspan)
Finally, the grid layout definition and nodes span can be adjusted for different screen sizes and make it responsive.
<grid-layout>
<grid-layout-definition>
<grid-layout-rows>
<grid-layout-row height="auto" />
<grid-layout-row height="auto"/>
<grid-layout-row height="auto"/>
</grid-layout-rows>
<grid-layout-columns>
<grid-layout-column width="auto"/>
<grid-layout-column width="auto"/>
<grid-layout-column width="auto"/>
</grid-layout-columns>
</grid-layout-definition>
<grid-layout-content>
<card grid:colspan="2">
<text>Card 1</text>
</card>
<card>
<text>Card 2</text>
</card>
<card>
<text>Card 3</text>
</card>
<card grid:rowspan="2" grid:colspan="2">
<text>Card 4</text>
</card>
<card>
<text>Card 5</text>
</card>
</grid-layout-content>
</grid-layout>

In this example, a grid with 3 rows and 3 columns is created. Each column/row has an auto size which means that each cell of a row/column will take as much space as its siblings.
Card 1 has a grid:colspan attribute set to 2 which means that the cells rendered will take the width of two columns.
Card 4 has the same grid:colspan and grid:rowspan value set to 2. It means that it will be rendered on a 2x2 sized cell.
The others cards take the space of the next available cell. After Card 4 takes it's space, the next available cell is on third row, first column. That is where Card 5 is rendered.
Warning
Directives can't be used on grid-layout-definition (and its children nodes) or grid-layout-content nodes because these tags are only used to configure the grid layout.
Warning
The grid:colspan and grid:rowspan directives written on the root of a partial-template placed in grid-layout-content are ignored, with a warning at generation: a partial template is analyzed on its own, without knowing where it is used. Wrap the partial template in an element that carries them, for example <card grid:colspan="2"><partial-template name="Summary" /></card>.
Grid definition
Node name : grid-layout-definition
The grid layout definition have to be put inside a grid-layout-definition node :
<grid-layout-definition gap="large">
<grid-layout-rows>
<!-- rows definition -->
</grid-layout-rows>
<grid-layout-columns>
<!-- columns definition -->
</grid-layout-columns>
</grid-layout-definition>
You can also specify a gap size between each cell of the grid with the optional attribute gap.
Values : none (default), small, medium, large, extralarge
Rows
Node name : grid-layout-row
For each row of the grid, the grid-layout-rows node has to contain a grid-layout-row node with an height attribute set to one of the following values :
| Value | Effect |
|---|---|
n |
Fixed height of n pixels. n is an integer. |
n% |
Fixed height of n percent of the grid layout's total height. n is an integer. |
fit |
Adapts the height to the content of the row. The row height matches the height of the tallest element it contains. |
* or n* |
All rows using a height of * or n* share the grid-layout height left free by rows with a fixed height. If a row has a height of 3*, it will take 3 times the height of a row with a height of *. If two rows have have the same value, for example 3*, they have the same height. n is an integer. |
auto (default) |
The behavior depends on the presence of other rows with a height that can fill the height left free by rows with a fixed height. When no rows can fill empty space, auto makes the row height fill the empty space (same behavior as *). When other rows can fill empty space, auto makes the row height fit the tallest element it container (same behavior as fit). |
<grid-layout-definition>
<grid-layout-rows>
<grid-layout-row height="*" />
<grid-layout-row />
</grid-layout-rows>
<grid-layout-columns>
<!-- columns definition -->
</grid-layout-columns>
</grid-layout-definition>
In this example, the first row of the grid will take the available height of the grid. The second row will take as much height as its tallest cell.
Columns
Node name : grid-layout-column
For each column of the grid, the grid-layout-columns node has to contain a grid-layout-column node with an width attribute set to one of the following values :
| Value | Effect |
|---|---|
n |
Fixed width of n pixels. n is an integer. |
n% |
Fixed width of n percent of the grid layout's total width. n is an integer. |
fit |
Adapts the width to the content of the column. The column width matches the width of the widest element it contains. |
* (default) or n* |
All columns using a width of * or n* share the grid-layout width left free by columns with a fixed size. If a column has a width of 3*, it will take 3 times the width of a column with a width of *. If two columns have the same value, for example 3*, they have the same width. n is an integer. |
auto |
The behavior depends on the presence of other columns with a width that can fill the width left free by columns with a fixed width. When no columns can fill empty space, auto makes the column width fill the empty space (same behavior as *). When other columns can fill empty space, auto makes the column width fit the widest element it container (same behavior as fit). |
<grid-layout-definition>
<grid-layout-rows>
<!-- rows definition -->
</grid-layout-rows>
<grid-layout-columns>
<grid-layout-column width="*" />
<grid-layout-column width="*" />
<grid-layout-column width="*" />
</grid-layout-columns>
</grid-layout-definition>
In this example, each column of the grid will adjust their width to share the available space equitably.
Note
The width attribute can be omitted in this example because * is the default value.
Auto-generated rows
If you don't know how many rows will be needed to render the nodes (for example when using a repeater on a datasource), you can define an automatic row height within the grid-layout-rows node.
This parameter is taken into account only for the rows created after the potentially defined rows.
<grid-layout>
<grid-layout-definition>
<grid-layout-rows auto-height="*">
<grid-layout-row height="50" />
</grid-layout-rows>
<grid-layout-columns>
<grid-layout-column width="auto" />
<grid-layout-column width="auto" />
</grid-layout-columns>
</grid-layout-definition>
<grid-layout-content>
<heading grid:colspan="2">Heading</heading>
<repeat values="collection">
<text>$Item</text>
</repeat>
<!--
... It will render :
<text>CONTENT 1</text>
<text>CONTENT 2</text>
<text>CONTENT 3</text>
<text>CONTENT 4</text>
<text>CONTENT 5</text>
-->
</grid-layout-content>
</grid-layout>
In this example, the first row will render the heading node on a full width row with a 50px height.
For the next nodes, created by a repeater, each node will use a new cell on a row with height adjusted to its content (as defined by the auto-height="*" attribute).
Nodes span
By default, each node will render inside the next available cell of the grid.
If you want to span a node across several rows and/or columns, you can use grid:rowspan and grid:colspan directives with a positive integer value.
<grid-layout>
<grid-layout-definition>
<!-- grid layout definition -->
</grid-layout-definition>
<grid-layout-content>
<card grid:rowspan="4" grid:colspan="2">
<text>Card</text>
</card>
</grid-layout-content>
</grid-layout>
In this example, the card will use the next available cells to fill a 4 cells high and 2 cells wide space.
Responsive grid layout
The responsiveness of the grid layout works with the use of breakpoints.
The idea is to first define a grid layout for the smallest screen (eg: mobile), then to override definitions when a larger screen need another layout configuration.

Breakpoints
To differentiate the different devices, we use the following breakpoints :
| Breakpoint name | Devices |
|---|---|
| (default) | Mobile devices |
small |
iPads, Tablets |
medium |
Small screens, laptops |
large |
Desktops, large screens |
extralarge |
Extra large screens, TV |
extraextralarge |
Very large screens/TV |
Grid definition
In order to define a specific grid definition in addition to the default one, we can use a grid-layout-definition node with the attribute viewport set to the corresponding breakpoint.
<grid-layout>
<!-- Default grid layout definition (mobile) -->
<grid-layout-definition>
<grid-layout-columns>
<grid-layout-column width="auto" />
</grid-layout-columns>
<grid-layout-rows auto-height="*" />
</grid-layout-definition>
<!-- Large grid layout definition (for Desktop) -->
<grid-layout-definition viewport="large" >
<grid-layout-columns>
<grid-layout-column width="*" />
<grid-layout-column width="*" />
<grid-layout-column width="*" />
</grid-layout-columns>
<grid-layout-rows auto-height="*" />
</grid-layout-definition>
<grid-layout-content>
<!-- Layout content -->
</grid-layout-content>
</grid-layout>
In this example, the grid will have one single column adjusted to its content for devices smaller than a desktop. Then if the screen size is larger, the grid will have tree equally sized columns.
Custom breakpoint thresholds
Each grid-layout-definition can override the pixel threshold at which its breakpoint activates, with the threshold attribute:
<grid-layout>
<grid-layout-definition>
<!-- Default grid layout definition (mobile) -->
</grid-layout-definition>
<grid-layout-definition viewport="large" threshold="900">
<!-- Large grid layout definition, now activating at 900px instead of the standard 1024 -->
</grid-layout-definition>
<grid-layout-content>
<!-- Layout content -->
</grid-layout-content>
</grid-layout>
threshold only applies to a grid-layout-definition whose viewport is set to a real breakpoint tier (small, medium, large, extralarge or extraextralarge) — it cannot be used on the default (mobile) definition or on viewport="extrasmall". A tier without its own threshold keeps the standard value from the table above, so you only need to declare it for the tiers you actually want to move — there is no need to repeat every value when overriding just one or two.
threshold is a bound value: it can reference a field or computed, and a later change to it updates the grid live.
Column / Row span
To define responsive behavior for the row and column span, you can use the breakpoint name as a suffix.
<grid-layout>
<grid-layout-definition>
<!-- grid layout definition -->
</grid-layout-definition>
<grid-layout-content>
<card grid:rowspan="4" grid:rowspan-medium="2" grid:rowspan-extralarge="1">
<text>Card</text>
</card>
</grid-layout-content>
</grid-layout>
In this example, the card will take 4 rows height for devices smaller than laptop, then 2 rows for devices smaller than large screen, then only one row.
The same principle applies to the colspan directive.
Responsive scope
By default, the breakpoints above react to the browser window's width. Setting breakpoint-scope="container" on the root <grid-layout> makes them react to the grid's own rendered width instead — useful when the grid lives inside a panel, modal, or sidebar narrower than the page.
<grid-layout breakpoint-scope="container">
<grid-layout-definition viewport="small" threshold="400">
<!-- ... -->
</grid-layout-definition>
<grid-layout-definition viewport="large" threshold="900">
<!-- ... -->
</grid-layout-definition>
<!-- grid-layout-content -->
</grid-layout>
Custom threshold values (see Custom breakpoint thresholds above) are not limited to container scope: they also apply in the default window scope, where they override the pixel thresholds the browser window width reacts to. It only has no effect in window scope when no grid-layout-definition declares a threshold at all, which keeps the default breakpoints as plain CSS.
<grid-layout>
<grid-layout-definition viewport="small" threshold="400">
<!-- ... -->
</grid-layout-definition>
<!-- other grid-layout-definition / grid-layout-content -->
</grid-layout>
breakpoint-scope decides once, when the grid is created, whether and how its width gets measured. If it is bound to a field, changing that field's value later has no effect on an already-rendered grid.
A threshold is different: if the grid already measures its width at creation (container scope, or window scope with at least one threshold set), a later change to a bound threshold field still updates the thresholds live. It only has no effect if the grid started without measuring its width at all (window scope with no threshold at all).
If an invalid combination of thresholds is reached at runtime (a tier's value not strictly above the previous one, once framework defaults are applied to every tier without its own threshold), it is ignored — with a console warning — and the standard thresholds are used instead for that render.
Both container scope and a custom threshold in window scope replace the standard, free CSS media queries with a JavaScript-driven measurement (a ResizeObserver in container scope, a window resize listener otherwise). The cost per grid is small, but avoid enabling either on a very large number of simultaneously-rendered grids (for example, one per row of a long list) — the default window scope without a custom threshold remains pure CSS.