Create custom list views
Learn how to build list views that dynamically repeat using your data.
The List View component allows you to create repeatable rows of data whose look and feel are defined by you.
The List View component is similar to a Container in that you can nest other components inside of it. You control the number of rows, which determines the number of times a component appears in a list view.
Table components and modules do not support nesting within a List View.
Demo
Try out the app to see list views in action (buttons are disabled). The app incorporates query data to display a list of orders, and buttons to request a return.
The demo uses data generated from Retool's API Generator. You can generate data yourself to follow along by clicking Generate API and saving the endpoint URL.
Display query results in a list view
List View components are empty when you first drag them onto the canvas. The default number of rows is three, so when you add a component like Button to a list view, the button repeats three times.
You can set the number of rows in a list view dynamically to match the number of rows returned in a query. This ensures that only the exact number of list rows are used.
Set the number of rows
Setting the number of rows dynamically ensures your list view is the correct size, even if you don't know the size of your result set.
For example, to retrieve the number of rows to show in your list view, use the generated API to retrieve all orders.
- Create a query using a RESTQuery resource and name the query
getOrders
. - Select the GET Action type and enter the URL of the endpoint you generated.
- Click Run to confirm the query ran successfully.
In the Inspector under Basic, you can now set {{ getOrders.data.length }}
in the Number of rows field.
This adds more rows to the list view, but the same data is shown in each row. If you inspect a row, you can see the Text values are static. Similar to setting the number of rows, you can use query data and the i
variable to dynamically set component properties like Text.
The i
variable
i
variableThe i
variable allows you to reference the index of an item within a List View component. This allows you to render and access unique values for each row.
In this example, the data returned in getOrders
includes the product SKU, customer email, and return status. You can use the i
variable to access individual items within getOrders.data
.
To show the customer email for each product, add a Text component to the list view and update its Value to {{ getOrders.data[i].customer }}
. The same applies for the product SKU, using getOrders.data[i].product_id
.
You can also use the i
variable in queries. For example, you might want to add a button that allows users to mark orders with a requested return. To do this, write a query that updates the record, setting return_requested
, using the i
variable to reference the product's ID.
You can then add an event handler to trigger the query when the button is clicked. When a component inside a list view triggers a query, the value of i
is substituted with the row index of that component. In this example, that's the index of the Button component.
Nested List Views
Nested List Views are available on Retool Cloud and Self-hosted Retool v2.98.2 and later.
You can nest List Views to dynamically generate UI elements based on multidimensional arrays and tree-like data structures—for example, org charts or threaded comments. Nested List Views support up to three levels of depth and can also reference child components using componentName.value
.
The following demo app shows one level of nested List Views to represent an org chart. It uses a two-dimensional array, which consists of a list of teams, and within each team, its name and a list of team members. You can download and import the app as JSON to learn how it's built.
The transformedJS
and transformedSql
queries convert sampleData
into an object you can use in a nested List View.
return {
name: ["Bob", "Kate", "Sarah", "Ali", "Joe", "Lisa", "Lenny"],
team: [
"Engineering",
"Engineering",
"Marketing",
"Sales",
"Engineering",
"Sales",
"Marketing",
],
};
const aggregatedMembers = sampleData.data.reduce((aggregator, row) => {
const nameArray = aggregator[row.team] ?? [];
aggregator[row.team] = [...nameArray, row.name];
return aggregator;
}, {});
return Object.entries(aggregatedMembers).map(([team, members]) => ({
team,
members,
}));
select team, array(name) as members from {{sampleData.data}} group by team
In nested List Views, ri
is an array of indexes for each nested level. For example, in the demo app:
ri[0]
contains indexes for the list of team namesri[1]
contains indexes for the list of team members
To access or iterate over data, use the ri
variable as an index. In the example app, team members are shown dynamically using transformedData.data[ri[0]].members[ri[1]]]
as the value of the team member text.
You can also use i
to refer to the last element, or the deepest level, in ri
. If you nest List Views to three levels, i
is equivalent to ri[2]
.
To access a component within a nested List View, you can reference componentName.value
. To access a component outside of the List View, you can pass a specific index—for example, textInput[1][2]
accesses the third text input in the second nested List View. Providing indexes is less performant than using componentName.value
.
Access nested List View items within queries
You can reference ri
and i
within a query if it is triggered by a component within a nested List View. For example, if you configure an event handler on a Button component within a nested List View to trigger the query, the button's ri
and i
values will be available to that query.
Updated 18 days ago