Table

The Table component displays rows of structured data in a tabular format.

Basic usage

A Table displays a list of data rows for each column.
Each column must contain an accessor. An accessor indicates which field of the data should be rendered under the column. For example, the following table has a column with accessor name, so row["name"] appears in the column.
tsx
Copied
1
<Table
2
title="Cats"
3
defaultPageSize={3}
4
columns={[
5
{
6
label: "Name",
7
accessor: "name",
8
},
9
{
10
label: "Breed",
11
accessor: "breed",
12
},
13
]}
14
data={[
15
{ name: "Graham Cracker", breed: "British Shorthair" },
16
{ name: "Bootz", breed: "American Wirehair" },
17
{ name: "Hazel", breed: "Taby" },
18
{ name: "Xiaohuang", breed: "Abyssinian" },
19
{ name: "Peaches", breed: "Birman" },
20
{ name: "Baosky", breed: "British Shorthair" },
21
]}
22
/>

Task backed

A Table can be backed by an Airplane Task rather than by the columns and data props. Set the task prop to call an Airplane task. The columns and data are automatically inferred from the output of the task.
tsx
Copied
1
<Table title="Cats" defaultPageSize={3} task="list_cats" />

Customizing data and columns

The columns prop sets the column configuration for both task-backed and non-task-backed tables. If columns and task are both set, then the column information inferred from the task output is ignored.
The columns of a table can be transformed with the columnsTransform prop. columnsTransform is a function that takes the table columns as args and returns the new, transformed columns.
outputTransform is like columnsTransform but for the table data. It is a function that takes the table data as args and returns the new, transformed data.
The following example illustrates customizing both the columns and data of a task backed table. The column name gets a custom label so that the column is titled with Cat Name rather than name. We then use outputTransform to capitalize the name of the cat in each data row.
tsx
Copied
1
<Table
2
title="Cats"
3
defaultPageSize={3}
4
task="list_cats"
5
columnsTransform={(cols) =>
6
cols.map((c) => (c.accessor === "name" ? { ...c, label: "Cat Name" } : c))
7
}
8
outputTransform={(data) => data.map((d) => ({ ...d, name: d.name.toUpperCase() }))}
9
/>

Filtering columns

The most common column transform is to hide columns from the table. This can be accomplished through columnsTransform, but you can use hiddenColumns for a more concise filtering syntax.
tsx
Copied
1
<Table title="Cats" defaultPageSize={3} task="list_cats" hiddenColumns={["breed"]} />

Column width

The column width and minimum width can be set using width and minWidth respectively.
tsx
Copied
1
<Table
2
title="Cats"
3
columns={[
4
{
5
label: "Name",
6
accessor: "name",
7
width: 50,
8
},
9
{
10
label: "Born",
11
accessor: "born",
12
type: "date",
13
minWidth: 200,
14
},
15
{
16
label: "Polydactyl",
17
accessor: "polydactyl",
18
width: 70,
19
},
20
]}
21
data={data}
22
/>

Column types

The type of a table column specifies how data in that column is rendered, edited and sorted.
Each column of a table is typed. Column types are automatically inferred from the data in the column. You can also manually specify the type of a column if it cannot be inferred.
tsx
Copied
1
<Table
2
title="Cats"
3
defaultPageSize={3}
4
columns={[
5
// Automatically typed as a string
6
{
7
label: "Name",
8
accessor: "name",
9
},
10
// Manually typed as a date
11
{
12
label: "Born",
13
accessor: "born",
14
type: "date",
15
},
16
// Automatically typed as a boolean
17
{
18
label: "Polydactyl",
19
accessor: "polydactyl",
20
},
21
// Automatically typed as json
22
{
23
label: "Notes",
24
accessor: "notes",
25
},
26
]}
27
data={data}
28
/>

Supported column types

TypeDisplayEdit Display
string (default)StringTextInput
numberNumberNumberInput
booleanCheckboxCheckbox
dateFormatted date stringDatePicker
datetimeFormatted date-time stringDateTimePicker
selectStringSelect
jsonCodeJSON editor

Column type options

Some column types can be configured with additional information using the typeOptions prop to customize how they are rendered or edited.
For example, to bound the min and/or max of a column of type number, set the numberMin and numberMax options respectively. To set the options for a column of type select, set the selectData option.
tsx
Copied
1
<Table
2
title="Cats"
3
defaultPageSize={3}
4
columns={[
5
{
6
label: "Name",
7
accessor: "name",
8
},
9
{
10
label: "Age",
11
accessor: "age",
12
canEdit: true,
13
typeOptions: { numberMin: 0, numberMax: 100 },
14
},
15
{
16
label: "Mood",
17
accessor: "mood",
18
type: "select",
19
typeOptions: {
20
selectData: ["happy", "sad", "neutral"],
21
},
22
canEdit: true,
23
},
24
]}
25
data={data}
26
rowActions={[
27
({ row }) => (
28
<Button variant="subtle" compact onClick={() => alert(`Meow ${row.name}`)}>
29
Pet
30
</Button>
31
),
32
]}
33
/>

Custom column components

Use a custom component to render a column when you want to display something that is not included as a built-in column type.
Use the Component field in the column definition to render a custom component.
tsx
Copied
1
// This is a custom component rendered in the `breed` column of the table.
2
const CatBreedLink = ({ value }) => {
3
const catString = value.replace(/ /g, "_") + "_cat";
4
return <Link href={`https://en.wikipedia.org/wiki/${catString}`}>{value}</Link>;
5
};
6
7
const TableWithCustomComponent = () => {
8
return (
9
<Table
10
title="Cats"
11
defaultPageSize={3}
12
task="list_cats"
13
columns={[
14
{ accessor: "name", label: "Cat Name" },
15
{ accessor: "breed", label: "Breed", Component: CatBreedLink },
16
]}
17
/>
18
);
19
};
When using custom components, make sure that they are defined outside of the component you are using them in. Components defined inline will be re-created on every render, causing performance issues.

Row selection

Row selection allows the user to select one or more rows of the Table. You can then use component state to access the selected rows. Selected rows can be passed as task parameters or used elsewhere in the view.

Single selection

Single row selection is enabled by setting the rowSelection prop to single. The selected row can be accessed using the selectedRow field on the component state.
tsx
Copied
1
<Table
2
title="Cats"
3
id="catsTable"
4
defaultPageSize={3}
5
columns={columns}
6
data={data}
7
rowSelection="single"
8
/>

Multi selection

Multi row selection is enabled by setting the rowSelection prop to checkbox. The selected rows can be accessed using the selectedRows field on the component state.
tsx
Copied
1
<Table
2
title="Cats"
3
id="catsTable"
4
defaultPageSize={3}
5
columns={columns}
6
data={data}
7
rowSelection="checkbox"
8
/>

Default selection

If row selection is enabled, you can also set which rows are initially selected by setting the isDefaultSelectedRow prop to a selection function.
tsx
Copied
1
<Table
2
title="Cats"
3
id="catsTable"
4
defaultPageSize={3}
5
columns={columns}
6
data={data}
7
rowSelection="single"
8
isDefaultSelectedRow={(row, index) => index === 0}
9
/>

Controlled selection

You can fully control row selection by setting the isSelectedRow prop to a selection function, and using the onToggleRow and onToggleAllRows props to update the row selection state.
Proceed with caution if using controlled row selection. If the table data changes, you will likely need to reset the row selection or keep it in sync with the new data.
tsx
Copied
1
<Table
2
title="Cats"
3
id="catsTable"
4
defaultPageSize={3}
5
columns={columns}
6
data={data}
7
rowSelection="single"
8
isSelectedRow={(row, index) => index === selectedIndex}
9
onToggleRow={(row, index) => setSelectedIndex(index)}
10
/>
11
<Button
12
onClick={() =>
13
setSelectedIndex(selectedIndex % 3 === 2 ? selectedIndex - 2 : selectedIndex + 1)
14
}
15
>
16
Change selection
17
</Button>

Setting a row ID

Each row has a unique ID which helps track which row(s) are selected. If the table data changes (for example, a row is added or deleted), the row ID ensures that the correct row remains selected.
By default, the row ID is an id field on the row data, or the index of the row if there is no id field. To customize the row ID, set the rowID prop to the name of a field that uniquely identifies the row.

Row actions

Row actions are buttons (or other UI) that appear on each row and can take action on that row.
Specify the rowActions prop to add one or more actions to every row. A row action is a React component that has a prop row that contains the row data of the action.
If your row actions call Airplane tasks, create Task backed row actions.
tsx
Copied
1
<Table
2
title="Cats"
3
defaultPageSize={3}
4
columns={columns}
5
data={data}
6
rowActions={(props) => (
7
<Button variant="subtle" compact onClick={() => alert(`Meow ${props.row.name}`)}>
8
Pet
9
</Button>
10
)}
11
/>
Row actions can be arbitrary components, but most of the time, they will be buttons. As a shortcut, you can create row action buttons by just providing label and onClick. The above can be rewritten as follows.
tsx
Copied
1
<Table
2
title="Cats"
3
defaultPageSize={3}
4
columns={columns}
5
data={data}
6
rowActions={{
7
label: "Pet",
8
onClick: (row) => alert(`Meow ${row.name}`),
9
}}
10
/>
Row action link buttons do not have onClick, but are rather created when label and href are provided.
tsx
Copied
1
<Table
2
title="Cats"
3
defaultPageSize={3}
4
columns={columns}
5
data={data}
6
rowActions={{
7
label: "Wiki page",
8
href: (row) => `https://en.wikipedia.org/wiki/${row.breed.replace(/ /g, "_")}_cat`,
9
}}
10
/>
Row actions support a subset of button props: preset, variant, disabled, and color. See Reference for details.

Task backed row actions

Row actions can automatically call Airplane tasks. Use a task on the row action to generate a button that calls a task.
When the row action button is clicked, the task will be executed with the data of the row passed in as parameters. You can optionally include additional parameters that are merged with the row data.
In the following example, we create two row actions that execute the tasks pet_cat and feed_cat when clicked. Each task execution includes the row data as task params—we can assume that both tasks take in a param name.
tsx
Copied
1
<Table
2
title="Cats"
3
defaultPageSize={3}
4
columns={columns}
5
data={data}
6
rowActions={["pet_cat", { slug: "feed_cat", label: "Feed" }]}
7
/>
When row action in a task backed table executes successfully, the table automatically refreshes, calling its backing task. This ensures that the table is up to date even if the row action mutates data.

Row action menu

Row actions can be put in a kebab menu using the rowActionsMenu prop. This can be useful when there are too many row actions to render in one column. Here, we move the feed_cat task to the overflow menu.
tsx
Copied
1
<Table
2
title="Cats"
3
defaultPageSize={3}
4
columns={columns}
5
data={data}
6
rowActions="pet_cat"
7
rowActionsMenu={{ slug: "feed_cat", label: "Feed" }}
8
/>

Transforming parameters in row actions

If a row's data doesn't match the parameters for a task backed row action, you can use the rowTransform parameter to make it match.
In the following example, the task with slug feed_cat takes in two params: cat and food. We use rowTransform to convert the row data field name to cat and then calculate the food param.
tsx
Copied
1
<Table
2
title="Cats"
3
defaultPageSize={3}
4
columns={columns}
5
data={data}
6
rowActions={[
7
{
8
slug: "feed_cat",
9
label: "Feed",
10
rowTransform: (row) => ({
11
cat: row.name,
12
food: favoriteFoods.get(row.name),
13
}),
14
},
15
]}
16
/>

Row action confirmation dialog

To show a confirmation dialog before the row action's action (onClick or task) is executed, set the confirm prop.
tsx
Copied
1
<Table
2
title="Cats"
3
defaultPageSize={3}
4
columns={columns}
5
data={data}
6
rowActions={{ slug: "pet_cat", confirm: true }}
7
/>
The dialog title, body, and buttons can be customized.
tsx
Copied
1
<Table
2
title="Cats"
3
defaultPageSize={3}
4
columns={columns}
5
data={data}
6
rowActions={{
7
slug: "pet_cat",
8
confirm: {
9
title: "Are you sure you want to pet the cat?",
10
body: "The cat may not want to be pet",
11
confirmText: "Pet",
12
cancelText: "Take me back",
13
},
14
}}
15
/>

Editing rows

Rows in a Table can be edited by setting a column's canEdit field to true. When a cell is editable, clicking on the edit icon will toggle the cell into edit mode based on the Column type. Cells that have been edited will have a small dirty indicator in the upper right corner.
Once a row is edited, you will commonly use a Row action to persist the edit by calling a task with the edited row.
In the following example, once a row is edited, the user clicks Update which calls the update_cat task. The edited row is passed in as task parameters.
tsx
Copied
1
<Table
2
title="Cats"
3
defaultPageSize={3}
4
columns={[
5
{
6
label: "Name",
7
accessor: "name",
8
canEdit: true,
9
},
10
{
11
label: "Born",
12
accessor: "born",
13
type: "date",
14
canEdit: true,
15
},
16
{
17
label: "Mood",
18
accessor: "mood",
19
type: "select",
20
typeOptions: {
21
selectData: ["happy", "sad", "neutral"],
22
},
23
canEdit: true,
24
},
25
{
26
label: "Polydactyl",
27
accessor: "polydactyl",
28
canEdit: true,
29
},
30
]}
31
data={data}
32
rowActions={{ slug: "update_cat", label: "Update" }}
33
/>

Editing custom column components

Custom column components can also integrate with the row edit system. This requires providing both a Component and an EditComponent.
  • The Component accepts a value prop and a startEditing callback. Component must call startEditing to toggle into edit mode.
  • The EditComponent accepts a defaultValue prop and a finishEditing callback. EditComponent must call finishEditing to toggle back into presentation mode.
Here, we provide a simple example with a custom, editable column component.
tsx
Copied
1
const TableEditCustom = () => {
2
return (
3
<Table
4
title="Cats"
5
defaultPageSize={3}
6
columns={[
7
{
8
label: "Name",
9
accessor: "name",
10
canEdit: true,
11
},
12
{
13
label: "Mood",
14
accessor: "mood",
15
Component: Mood,
16
EditComponent: EditMood,
17
canEdit: true,
18
},
19
]}
20
data={data}
21
rowActions={{ slug: "update_cat", label: "Update" }}
22
/>
23
);
24
};
25
26
const Mood = (props: { value: string; startEditing: () => void }) => {
27
return (
28
<Stack direction="row" justify="space-between" grow>
29
<Label>{props.value}</Label>
30
<Button variant="subtle" compact onClick={props.startEditing}>
31
Edit
32
</Button>
33
</Stack>
34
);
35
};
36
37
const EditMood = (props: { defaultValue: string; finishEditing: (newValue: string) => void }) => {
38
return (
39
<Select
40
data={["happy", "sad", "neutral"]}
41
defaultValue={props.defaultValue}
42
onChange={(v: string) => props.finishEditing(v)}
43
withinPortal
44
initiallyOpened
45
autoFocus
46
/>
47
);
48
};

Component API

columns
optional
(keyof TRowData | Column<TRowData>)[]
Sets the table columns. If the table is task-backed, this field overrides the columns inferred from the data.
Column
columnsTransform
optional
(columns: Column<TRowData>[]) => Column<TRowData>[]

Callback used to modify the table columns.

data
optional
TRowData[]

The data, or rows, to display in the table.

defaultPageSize
optional
Default
10
number

Sets the page size on initial render.

enableCSVDownload
optional
string | false | true

Adds a "download as CSV button" to the table footer. If a string is provided, it will be used as the file name with a .csv extension appended.

error
optional
string

Renders an error message.

freezeRowActions
optional
Default
true
boolean

Freezes the row actions column when true.

grow
optional
boolean

If true, the element will grow to fill available space.

This prop works only if the element is a direct child of a Stack.

height
optional
Default
auto
SizingToken | "{number}px"
Defines the height of the component. See SizingToken docs for more details.
hiddenColumns
optional
string[]

Columns to hide in the table. Reference columns using the column accessor, or the inferred output field for task backed tables.

id
optional
string

The ID referenced by the global component state.

isDefaultSelectedRow
optional
(row: TRowData, rowIndex: number) => boolean

Function to choose selected rows on initial render.

isSelectedRow
optional
(row: TRowData, rowIndex: number) => boolean

Function for controlled row selection.

loading
optional
boolean

Renders a loading indicator when true.

noData
optional
string

The message to display when the table has no data.

onToggleAllRows
optional
(value: boolean) => void

This is called when the toggle-all-selected-rows checkbox is pressed when rowSelection is set to "checkbox", with the value of the resulting checkbox.

If isSelectedRow is set, and this is not provided, the toggle-all-selected-rows checkbox will not be shown.

onToggleRow
optional
(row: TRowData, idx: number) => void

This is called when the selection state of a row is toggled. Passes in the row data as well as the index of the row in the table.

outputTransform
optional
(output: TOutput) => TRowData[]

Callback to transform the task output.

rowActions
optional
TaskRowAction | BasicRowAction | ComponentRowAction | Array<TaskRowAction | BasicRowAction | ComponentRowAction>
Adds components to the end of each row. We support three types of row actions:
  • Use TaskRowAction to add a task-backed button that uses the row contents as task parameters.
  • Use BasicRowAction to add a link button or a regular button, depending on whether href or onClick is provided.
  • Use ComponentRowAction if you want to fully customize your row action.
Type definitions
rowActionsMenu
optional
TaskRowAction | BasicRowAction | ComponentRowAction | Array<TaskRowAction | BasicRowAction | ComponentRowAction>
Adds components to an overflow menu at the end of each row. See documentation for rowActions for details on each type.
rowActionsWidth
optional
number

If set, the row actions column will be set to this width, in pixels.

rowID
optional
Default
id or the index of the row
string

A consistent, unique field used to identify a row. This is used to ensure that row selection is consistent even when the data changes (e.g. when a row is added or deleted).

If not provided, defaults to an "id" field on the row, or the index of the row if there is no id.

rowSelection
optional
"checkbox" | "single"

"single" enables selection of single row while "checkbox" adds a checkbox one each row and enables selection of multiple rows.

selectAll
optional
Default
true
boolean

If true, a select all checkbox is shown that allows selecting all rows. Only applicable if rowSelection="checkbox".

showFilter
optional
Default
true
boolean

Allows for global filtering when true.

task
optional
string | AirplaneFunc | SlugQuery | FunctionQuery

The task query to execute when this component loads. The component's data will be populated by the task's output and loading and error states will be handled automatically.

If the task doesn't require any parameters or special options, you can just pass the task slug (task="my_task") or an AirplaneFunc—the return value of airplane.task() (task={myTask}).

If the task does require parameters or special options, pass the task as an object. If you are using a slug, specify the slug prop to pass the task configuration as a SlugQuery. If you are using an AirplaneFunc, specify the fn prop to pass the task configuration as a FunctionQuery.

SlugQuery
FunctionQuery
title
optional
string

Sets the title above the table.

width
optional
Default
auto
SizingToken | "{number}px"
Defines the width of the component. See SizingToken docs for more details.

State API

clearSelection
optional
() => void

Clears all selected rows

rowActionResult
optional
{ output?: TOutput; loading?: boolean; error?: ExecuteTaskError }

The result of the latest executed row action

selectedRow
optional
TRowData
A single row that has been selected. Only applicable if rowSelection="single"
selectedRows
optional
TRowData[]

Rows that have been selected