Multiple Input Group
An Array type defines a repeatable set of fields.
It allows users to fill in the same sub-form multiple times, producing an array of objects in the output data.
This is typically used when you need to collect several entries that share the same structure — for example, a list of members, vehicles, or addresses.
Each iteration represents one complete instance of the defined structure.
Users can add or remove items dynamically through the interface.
- The internal components behave like standard form fields (supporting validations, visibility, and conditions).
- Each array item is independent — changes in one item do not affect others.
- The way the array is rendered (
arrayType) can vary (e.g. as a table or list) depending on the configuration. - When the array is empty, the
empty_labeltext is displayed if defined.
Example
{
"member_list": {
"type": "array",
"label": "",
"arrayType": "table",
"array_actions": {
"max": 2,
"add_label": "Add member",
"title": "Member",
"remove_label": "Remove member",
"empty_label": "No member",
"actions_title": "Remove"
},
"properties": {
"name": {
"label": "Name",
"component": "text"
},
"did_agree": {
"label": "Did agree",
"component": "true_false"
},
"accept_terms": {
"label": "Accept terms",
"component": "checkbox"
},
"date_of_birth": {
"label": "Date of birth",
"component": "date"
}
}
}
}
This configuration defines a repeatable list of members.
The user can add up to two members, each represented as a row in a table layout.
Output structure
The resulting value is an array of objects.
Each element corresponds to one filled instance of the defined properties.
Example output:
[
{
"name": "Alice",
"did_agree": true,
"accept_terms": true,
"date_of_birth": "1992-03-15"
},
{
"name": "Bob",
"did_agree": false,
"accept_terms": false,
"date_of_birth": "1988-07-02"
}
]
Supported keys
| Key | Required | Description |
|---|---|---|
type | ✅ | Must be set to array. |
label | ❌ | Display label shown above the group. |
arrayType | ❌ | Defines how the list is visually displayed (table, list, block, etc.). |
array_actions | ❌ | Object controlling add/remove behavior, labels, and item limits. |
properties | ✅ | Definition of the repeated fields (same structure as a group). |
default | ❌ | Initial array of objects to pre-fill the list. |
visible | ❌ | Boolean or array of conditions controlling visibility. |
readOnly | ❌ | Disables add/remove actions and editing of existing items. |
array_actions object
The array_actions key defines how the user interacts with the array — adding, removing, or viewing items.
It also allows you to control item limits and customize button labels.
Example
"array_actions": {
"max": 3,
"min": 1,
"add_label": "Add member",
"remove_label": "Remove",
"empty_label": "No member available",
"title": "Member",
"actions_title": "Actions"
}
Supported keys
| Key | Required | Type | Description |
|---|---|---|---|
max | ❌ | number | Maximum number of items allowed. If reached, the "Add" button is disabled. |
min | ❌ | number | Minimum number of items required. |
add_label | ❌ | string | Text shown on the "Add" button. |
remove_label | ❌ | string | Text shown on the "Remove" button for each item. |
empty_label | ❌ | string | Message displayed when the array is empty. |
title | ❌ | string | Title shown above each item (useful for list or block displays). |
actions_title | ❌ | string | Header label for the actions column (mainly used in table mode). |
add | ❌ | boolean | Default: true, enable add item |
remove | ❌ | boolean | Default: true, enable remove item |
min and max can be object with a from path, that way the minimum and / or the maximum iteration of the table can be controlled by the data
{
"min": {
"from": "minimum_defined_in_the_data"
}
}
Setting false to array_actions is a shortcut that acts like setting add and remove to false. No actions will be shown
Behavior
- Each new item is initialized based on the
propertiesdefinition. - Default values defined inside
propertiesare applied when creating a new item. - Items can be added or removed dynamically.
maxandminsettings inarray_actionsdefine the item limits.- Visibility and read-only states apply to the entire array component.
To handle conditions based on the current item see the related documentation here
A onValueChange can be applied to each iteations of the components see related documentation here
Count
or tracking and integration purposes, a special __count data property is automatically maintained for each multiple input group.
This property keeps track of the current number of items in the array and is updated whenever an item is added or removed.
This allows you to reference the count of entries dynamically elsewhere in the schema or logic conditions.
Example
{
"car_list": {
"type": "array",
"label": "",
"arrayType": "table",
"array_actions": {
"add_label": "Ajouter un article",
"title": "Articles",
"remove_label": "Supprimer",
"empty_label": "Pas d'articles",
"showRowId": true
},
"properties": {
"plate": {
"component": "text",
"label": "Plate"
}
}
}
}
When the form is filled, an internal data key is generated alongside the array value:
{
"car_list": [{ "plate": "ABC123" }, { "plate": "XYZ789" }],
"car_list__count": 2
}
Behavior
- The __count property is automatically updated whenever:
- A new row is added.
- An existing row is removed.
- The count reflects the current number of visible items in the array.
- This property can be used in conditions, calculations, or as a reference in other components.
- The count key is always appended to the array’s root key name using a double underscore ().
- It is a read-only internal value — users cannot modify it manually.
- This mechanism is useful for data interpolation or validation scenarios that depend on the number of entries.
Disabled
A disabled (see related doc) attribute can be defined on a multiple input group to make all of its fields non-editable.
When the component is disabled, every field inside its properties automatically inherits this state.
{
"member_list": {
"type": "array",
"arrayType": "table",
"disabled": true,
"properties": {
"name": {
"label": "Name",
"component": "text"
},
"age": {
"label": "Age",
"component": "number"
}
}
}
}
In this configuration, the entire table and all its inputs are disabled.
While it is possible to define disabled properties individually on each subfield, it is usually clearer and more maintainable to apply it directly at the component level.
Advanced : Data interpolation within table rows
It is possible to dynamically reference data within the same iteration.
This allows one field to depend on the value of another field from the same row — for example, to populate a select list based on another selected value.
This feature uses data interpolation through variable paths with iteration indexing.
Example
{
"car_list": {
"type": "array",
"label": "",
"arrayType": "table",
"array_actions": {
"title": "Cars"
},
"properties": {
"kind": {
"label": "Kind",
"component": "select",
"list": [
{ "value": "kind_a", "label": "Kind A" },
{ "value": "kind_b", "label": "Kind B" }
]
},
"usage": {
"label": "Usage list depending on kind",
"component": "select",
"list": {
"from": "usage_list.${car_list[x].kind}"
}
}
}
}
}
Explanation
In this configuration:
- The
car_listtable defines multiple cars. - Each row (iteration) contains two fields:
kindandusage. - The
usageselect list depends on the current row’skindvalue.
The key expression here is:
"from": "usage_list.${car_list[x].kind}"
${car_list[x].kind}dynamically refers to thekindvalue of the current row (xrepresents the current iteration index).- The system resolves this expression for each row individually, ensuring that the
usagelist is context-aware.
Behavior
- The expression is evaluated separately for each item of the array.
- Any change in the controlling field (here,
kind) triggers an update of the dependent field (here,usage). - You can interpolate any property within the same array iteration using the same
${car_list[x].property}syntax.
Filters
There is a way to filter data that will be shown onto the table.
We can define it by setting the filters proprety to the table. It accepts an array of filters.
The objective of the filter is to define an array of conditiions that will filter the data of the table. Only the matching items will be shown.
Filtering does not mean that excluded items will be destroyed, they are just not shown in the table
Example
{
"car_list": {
"type": "array",
"label": "",
"arrayType": "table",
"array_actions": {
"title": "Cars"
},
"filters": [
{
"property": {
"from": "car_list[x].kind"
},
"assertion": "equal",
"value": "compact"
}
],
"properties": {
"warning_color": {
"label": "Warning color",
"component": "true_false"
},
"plate": {
"label": "Plate",
"component": "text"
},
"kind": {
"label": "Kind",
"component": "text"
},
"price": {
"label": "Price",
"format": "price",
"type": "text",
"text": {
"from": "car_list[x].price"
},
"color": {
"color": "warning",
"conditions": [
{
"property": "car_list[x].warning_color",
"assertion": "isTrue"
}
]
}
}
}
}
}
In this example each item of the car_list will pass the filter condition and only the one with the property kind equal to compact will be shown.
Validations
Validations can be set to the whole type: array object.
{
"vehicle_list": {
"type": "array",
"label": "",
"arrayType": "table",
"array_actions": {
"title": "Vehicle"
},
"validations": ["required"],
"properties": {
"plate": {
"label": "Plate",
"component": "text"
},
"effective_date": {
"label": "Effective date",
"component": "date"
}
}
}
}
That way we can force the table to be competed.
If you have to make a validation of an item property based on an other property of this item, you can do this by pointing to the item index using [x]
{
"content": {
"type": "array",
"label": "",
"arrayType": "table",
"array_actions": {
"max": 10,
"add_label": "t:add_object",
"title": "t:object_title",
"remove_label": "t:remove_object",
"empty_label": "t:no_object"
},
"properties": {
"content": {
"label": "t:object_type",
"validations": ["required"],
"component": "select",
"list": {
"from": "constants.object_type_list"
}
},
"insured_amount": {
"label": "t:insured_amount",
"component": "price",
"validations": [
"required",
{
"assertion": "isBelow",
"value": {
"from": "content[x].content_selected_list_item.max_insured_amount"
},
"message": "t:trop"
}
]
}
}
}
}