Build tables

Learn how to display and interact with data using the Table component.


Updated Table component

This guide uses the legacy Table component. See the updated version of this guide for using the updated Table component.

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). The table incorporates different column types, and dynamically highlights cells in the Sales column if the value is lower than 200.

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 test data in JSON format. You can change the data displayed in a table by editing the Data property. Use the format to choose the query.

Display query data in a table

Change column types

When you add tables to the Canvas, their columns are set to Auto. You can set column types manually by clicking on the column in the Inspect tab and selecting the type from the Column type dropdown. You can also manage column types dynamically with the Use dynamic column settings option.

Rating column type

Editable table columns

After populating a table with data, you can edit cells and add rows with some additional configuration. To make a column editable, select a table to open the Inspect tab in the right panel. Scroll to the Columns section and select the boxes next to the columns you want to make editable.

Making columns editable

After making columns editable, you can double click on a cell to edit it. When you edit cells, changes are saved in the table's recordUpdates property. This tracks the changes you've made 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 an appropriate query to update your data. For example, saving changes to a PostgreSQL database can be done using a Bulk update query.

Creating a bulk update query

After creating the bulk update query, you need to connect it to the table. Open the Inspect tab again for the table and scroll to the Event handlers section. Add a Save changes event handler that runs the bulk update query.

Adding an event handler

Add new data to tables

You can add rows to tables and save the changes as new data. This allows users to add data without needing to build a separate group of inputs.

In the Inspect tab, you can enable the Show add row button option which adds a + button to your table. This allows users to add rows. Similar to editing cells, you need to create a query that updates your data source and an event handler to trigger the query when a row is added.


Columns also need to be editable to add rows.

To write back to your data source, write an insert query using the GUI editor.

Adding an insert query

After creating the insert query, you need to connect it to the table. Open the Inspect tab again for the table and scroll to the Event handlers section. Add a Save new event handler that runs the insert query.

Adding new row event handler

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

By default, tables include a refresh button that reloads its query. When users click the button, the query runs and the table updates to reflect the latest 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.

Adding an event handler to refresh table data

Custom columns

You can add custom columns to tables using the + Add button in the Inspect tab. 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 {{ currentRow.monthlyIncome * 12 }}

Adding a custom column

Custom columns have the same attributes that regular table columns have including:

  1. Column types
  2. Custom titles
  3. Background color
  4. HTML, overflow, sorting, and dynamic visibility

You can rename custom columns but each column still has a key that you can't edit. If you want to access the column mapper dynamically, you need to use the key, not the column name.


Accessing custom column data in other components

Custom column data isn't accessible in {{ }} or other properties of the table object. If you want to access custom column values in other components (e.g., using {{ }}, use a Query Transformer to calculate the additional values, which then propagates to your table.

Sort, filter, and customize data presentation

Tables come with prebuilt functionality for sorting and filtering data. You can click on a column to sort it, and there's a filter button in the lower right corner of the table that allows you to create custom filters. You can combine a table with a Text Input component to create a search field that serves as another way of filtering data.


Table filters on custom columns aren't currently supported.

Show and hide columns

When you click a column in the Inspect tab, there's a Show column toggle that you can switch on and off. You can also use dynamic column settings to hide columns.


In the Inspect tab, 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. This is recommended when working with large datasets to reduce the amount of data your table uses at a time, 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 sortedColumn and sortedDesc properties to achieve this.

Dynamic column settings

You can enable dynamic column settings by toggling the Use dynamic column settings on in the Inspect tab. This setting gives you additional control over table columns, including which columns are shown, how they're styled, etc.

When you enable dynamic column settings, a Column settings property is displayed. This property supports an array of strings (column keys) or objects, each overriding the settings for one column.

Dynamic column settings
Enabling the Apply to column order option overrides the current column order with the order passed in the Column settings property. If there isn't an object with a name key that matches a column, that column is hidden. These are the properties that you can override with dynamic column settings.

Certain settings are only evaluated when data changes. You must reload the table data when setting these values for changes to be reflected. Click the Reload button on the table if you've enabled it, rerun the query that is used by the table, or select Reset app state from the ••• App settings menu in the toolbar.

PropertyDescriptionEvaluated when data changes
typeSet the column type. Accepts all types except for dropdown.No
frozenFreezes a column. Accepts left and right.No
mapperSets the mapped value for a column. Supports JavaScript expressions with escaped brackets (e.g., {\{ self * 2 }\}.Yes
colorMapperSets the mapped value for the column's color. Supports JavaScript expressions with escaped brackets (e.g., "colorMapper":'{\{Math.abs(i % 2) == 1?"grey":"blue"}\}').Yes
restrictedEditingMapperBoolean value that restricts the editing of cell values within an editable column. If true, cell values cannot be edited. Supports JavaScript expressions with escaped brackets.Yes
modalIdOpens the modal of another modal column. If the key is invalid, a modal with that key still opens.Yes

Dynamic column types

These are the supported dynamic column type values.

  • boolean
  • currency
  • date
  • dateTime
  • html
  • imageUpload
  • imageUrl
  • json
  • link
  • markdown
  • modal
  • multilineText
  • number
  • percent
  • rating
  • tag
  • tags
  • text
  • time
  • userEmail

Hide columns

You define the columns to show by passing in an array of objects that map to the columns. If there isn't an object with a name key that matches a column, that column is hidden.

Hiding columns using dynamic column settings

Set column mappers

Column mappers allow you to change default column values. You can set them using the Mapped value on each column or with dynamic column settings. With dynamic column settings, you can use a string inside of the object for each column to map values, but you need to escape the {{ }} tag. For example, { "name": "email", "type":"default", "mapper":'{\{self+1}\}'} uses the current cell value (self) and adds 1 to it.

Defining a mapper


Dynamically created mappers only update when the table data or app loads, so if you edit the mapper value, you need to reload the data.

After setting a dynamic mapper, the Mapped value field on the column settings is greyed out and the value updates in realtime.

Setting a mapper

Set column background color

You can set background colors using the Styles section of the Inspect tab or with the colorMapper key. When you set the colorMapper key, alternate single or double quotes so that the outer quotes are not repeated inside the string. For example, "colorMapper":'{\{Math.abs(i % 2) == 1?"#D1D1D1":"#3C92DC"}\}'.

Setting the color mapper key

Common table variables

currentRow, currentColumn, self, and i are commonly used with tables. self 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 self is the value of the current cell.

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

Using self and current row

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.

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.