Skip to main content

Display and edit data with the Table component

Learn how to display and interact with data using tables.

Tables are a common way to view and interact with your data. You can sort, filter, paginate, and download rows of information. Tables also support row selection and insertion, server-side pagination, and custom columns.


Try out the app to see tables in action (edits are disabled).

Load data in a table

When you drag a Table component to the Canvas, it automatically displays data from one of your queries. If you don't have any queries, Retool initially populates the table with demo data. You can change the data displayed in a table by editing the Data source property. From the dropdown, you can select a query, use JavaScript (e.g.,, or input an array.

Change column types

When you add tables to the Canvas, Retool tries to infer the column types. You can also set column types manually by clicking the column in the right panel, and selecting the type from the Format dropdown.

Set a primary key

Retool recommends setting a primary key column to uniquely identify each row of data. If your data already has an ID column, Retool automatically uses it for the primary key. You can also manually set it to any column that is guaranteed to be unique.

If you don't want to show the primary key column in your table, select the column and set its Hidden property to true.

Set source keys

You can set an optional source key on each column to pull data from your data source. For example, you could set the source ID to populate the table column with data from the data source's ID column.

Editable table columns

After populating a table with data, you can make columns editable and change cell values. To make a column editable, click ••• next to the column, then select Make editable. You can also select a column and click the Editable checkbox.

Click a cell to edit its value. When you edit cells, changes are saved in the table's changesetArray property. This tracks the changes but it doesn't update the underlying data source that your query connects to. To save edits back to your data source, you must write a query to update your data. For example, saving changes to a PostgreSQL database can be done using a bulk update query.

After creating the bulk update query, you need to connect it to the table. Select the table and scroll to the Add-ons section. Add a Save actions add-on. Select the add-on, and then add an event handler that runs the bulk update query.

Add new data to tables

You can add rows to tables and save the changes as new data. To do this, drag in a Form component and click Generate form. This opens a modal where you can select the table you previously dragged onto the Canvas.

Have the form trigger an update query to add a new record.

Dynamic values in new rows

You can use dynamic values if you have columns you don't want users to edit, or if you want to automate some of the values added in a new row. These values are generated when the row is added, and don't require manual input from the user. For example, if your table has a timestamp column, you can use {{ moment() }} to generate the timestamp when the query runs.

Any {{ }} values can be used while creating new rows in a table

Automatically reload table data

If you want to automatically reload table data when changes are saved, add an event handler to the query that updates your data source. Configure the event handler so that when the query succeeds, it runs the query that populates your table.


Tables include a toolbar by default with filter, download, and refresh functionality. Click the Toolbar in the Add-ons section to add and remove toolbar buttons. You can add custom buttons and change the toolbar location as well.

Custom columns

You can add custom columns to tables using the + button above the list of columns. Custom columns are often used to calculate data based on other values in your Retool app. For example, if you have a monthlyIncome column and you want to calculate a yearlyIncome column, you could use {{ currentSourceRow.monthlyIncome * 12 }} as the value.

Custom columns have the same properties as regular table columns.

Configure actions

You can add actions that are displayed as buttons when hovering over a row in a Table. You can attach event handlers to actions to trigger queries, control components, or call other APIs.

Now when the button is clicked in the table, the modal opens.

Sort, filter, and customize data presentation

Tables come with prebuilt functionality for sorting and filtering data. You can click column headers to sort columns, and you can add toolbars to tables that include filter options.

You can also configure a standalone Filter component to filter table data. After adding the component, connect the filter to the table by enabling the filter's Link to table option and selecting the table.

To use JavaScript to manage filters, see the filterStack section.

Show and hide columns

When you click a column, there's a Hidden property that you can set to true or conditionally control with JavaScript.

Row selection

Single row selection is enabled by default, but tables also support multiple row selection, or you can disable it completely. Selecting multiple rows is often useful when your app needs to make bulk actions. Navigate to the Interaction > Row selection section to change these settings.


You can choose between Pagination and Scroll options for handling overflow items.

You can also enable server-side pagination. You write queries that include parameters to define a subset of data to retrieve, server-side. Retool recommends this when working with large datasets to reduce the amount of data your table uses, which can help your app's performance.

Sorting of tables using server-side pagination must be done on the server. You can write queries that use the table's sort or sortObject properties to achieve this.

To enable search for a table, drag a Text Input component to the Canvas. Next, click on the Table component and navigate to the Interaction section. Set the Search term property to {{ textInput1.value }} (the name of the Text Input component might be different in your app) to connect the Text Input component to the table. This enables search across all the table's columns, and Retool provides options for fuzzy, exact, and case insensitive searches.

Row selection persistence

Some applications need to track the table's selected rows even while the user is filtering, sorting, or paginating through data. You can enable this in the Interaction section by selecting the Persist row selection checkbox.

Common table variables

currentSourceRow, item, and i are commonly used with tables. item and i work with other components, but they have unique meanings when used with table column settings. With tables, i is the index of the current row in the original dataset, and item is the value of the current cell.

Variables like currentSourceRow and item are useful in situations where you want to access values in a table and then calculate a new value. For example, {{ item * 10 }} takes the current cell and multiplies it by ten. You can also do something like {{self + ' ' + '(Last sale: + currentSourceRow.last_sale)'}} to combine two values and map them to a column.

i is often used to iterate over a set of values and perform some kind of action. For example, you can iterate over each row in a table and update values, make API requests, send emails, etc. For example, this JavaScript transformer iterates over all rows in table1, and returns each sales person with over 200 sales.

JavaScript transformer
var salesPerson = {{ }};
var highSales = salesPerson.filter(i => i.sales > 200);
return highSales;

See the Table component reference documentation for more information on properties, events, and JavaScript methods that you can use with tables.


filterStack is an object that contains all the filters applied to the table. It includes a filters array with and and or operators for comparing filters. You can write JavaScript to configure the filterStack with these methods:

  • setFilterStack
  • clearFilterStack
  • setFilter
  • clearFilter
filters: [
{ columnId: "id", operator: "=", value: 1 },
{ columnId: "id", operator: "=", value: 2 },
operator: "and",

See the list below for all available operators. Some operators accept both text and symbol options.

  • lessThan, <
  • lessThanOrEquals, <=
  • greaterThan, >
  • greaterThanOrEquals, >=
  • equals, =
  • doesNotEqual, !=
  • is
  • isNot
  • includes
  • doesNotInclude
  • isTrue
  • isFalse
  • isEmpty
  • isNotEmpty
  • intersects
  • isOneOf
  • isNoneOf
  • isBefore
  • isAfter

See the Table component documentation for more information about table properties and methods.


You can use selectRow to select specific rows in a table. There are two modes you can use to specify the row: key and index. Use key to pass the primary key to select the row. Otherwise, use index and pass an indexType to select a row.

index accepts a single index or a list of indexes.

selectRow examples
// Selects the row with primary key 4

table1.selectRow({ mode: "key", key: 4 });

// Selects the row that represents[4]

table1.selectRow({ mode: "index", indexType: "data", index: 4 });

// Selects the fifth row visible on the table

table1.selectRow({ mode: "index", indexType: "display", index: 4 });

// Selects the rows that represent[3] and[4]

table1.selectRow({ mode: "index", indexType: "data", index: [3, 4] });