Data table

A data table shows read-only rows and columns, so readers can compare values and screen readers can browse them. Choose whether a phone sees a table that scrolls sideways or rows restacked as labeled blocks; sorting needs no wiring beyond marking a column sortable. Each table below wears its own variant and density.

SeedMood
CobaltCalm
VermilionBold
DishPriceHeat
Laksa$11Hot
Soba$9Mild
TeamPoints
Otters42
Herons37
TaskOwner
LoginAda
DocsGrace
DrinkPrice
Latte$4
Cortado$4
Mocha$6
TrackTime
Kite3:12
TrackTime
Lantern2:48

Reference docs⁠

BlazorReactWebOverviewExamplesAPIStylingAccessibility

Choosing a way to show a collection⁠

Three components show many items at once. Each suits a different job:

  • List: a column of items to scan and act on, such as contacts or settings.
  • Data table: read-only rows and columns to compare, such as an invoice or a price table.
  • Data grid: many rows the reader sorts, selects, and moves through by keyboard, such as an order list of thousands.

One job belongs elsewhere. Splitting a long collection into pages is a paginator, used alongside any of these.

Choosing a variant⁠

Four variants set how much a data table stands out from the page, from most to least:

  • Filled: a table that is the focus of its section, such as a price list on a pricing page, on the primary container color.
  • Tonal: a table that supports nearby content, such as a usage summary beside a chart, on the secondary container color.
  • Surface: a table that needs its own ground, such as a list in a panel of another color, on the lowest surface container color.
  • Standard: the default, with no fill, for a table that sits directly on the page or inside a card, such as an invoice list, and takes that container's color.

Built in⁠

  • A column marked sortable in columns sorts when its header is activated, and a screen reader announces the new order.
  • stack restacks each row as a block with every value beside its column title, so a narrow screen needs no sideways scrolling.
  • wrap-cells keeps every word of a long cell readable, by growing the row rather than cutting the text.
  • virtualize keeps a table of thousands of rows light.
  • Markup you already have works: a table written inside the element becomes a data table.
  • templateFields puts your own content in a cell, such as a link or a status label, through a child with a cell: slot name.

Using data tables well⁠

  • Give a numeric column the number or currency type, so its values sit at the end of the cell, and leave text columns at the start.
  • Give a currency column its currency code, such as "currency": "EUR". The table's locale decides how a price is written, not which currency it is in, and a column with no code shows plain numbers.
  • Keep column titles short, and move any explanation to the caption.
  • Put paging controls in a footer below the table, with a rows-per-page choice and a position readout such as 1–10 of 100, and slice the data yourself when the page changes.
  • Choose a data grid when rows need selecting, since a data table has no selection and no checkbox column.
  • Set stack to auto for a table people open on a phone, and leave it at the default for a table whose columns must stay aligned.

These rules follow Material 3 Expressive's guidance for data tables, on material.io.

Variants⁠

variant sets how much the table stands out; Choosing a variant says when to use each. Omit it and the table takes the variant from your style configuration, which is standard unless you have changed it.

Caption position⁠

The default slot holds the caption, which also serves as the table's name. caption-position places it before or after the rows; omit it and the table takes the position from your style configuration, which is leading unless you have changed it. With no caption, name the table with grid-label.

Density⁠

density sets how tightly rows are spaced, from 0 (the roomiest) to 4 (the tightest), five values in all. Omit it and the table takes the density from your style configuration, which is standard unless you have changed it.

Stack⁠

stack decides how a table fits a narrow width. never, the default, keeps rows as rows. always draws every row as a block with each value beside its column title, and auto does so only while the table cannot fit as rows. Each table below is held to 280 pixels.

Columns and rows⁠

items holds the rows, and columns describes the columns. Without columns, the table builds them from the first row's keys. Mark a column sortable and its header becomes a button; activating it cycles through ascending, descending, and unsorted. Set unique-identifier to the name of a field that identifies each row.

Lines and stripes⁠

striped, show-column-dividers, and borderless change how the table separates its content. Borderless removes the outer frame and keeps the dividers between rows and cells. Each switch below sets one of them.

Wrapping text⁠

By default a cell truncates text that does not fit. wrap-cells wraps it instead: rows grow to fit, and the table scrolls sideways when its content is wider than its space. The switch below turns wrapping on for a table held to 360 pixels.

Cell content⁠

templateFields lists the fields whose cells show content you supply rather than the value. Add a child with slot="cell:{rowKey}:{field}" for each cell, where rowKey is the row's value in the unique-identifier field. The table then needs that identifier.

Large data sets⁠

virtualize draws only the rows in view, so give the table a height to scroll within. overscan-count sets how many extra rows load beyond the visible area; keep it small. For rows that live on a server, assign itemsProvider from code: the table asks it for rows, and a table without virtualize asks once for all of them.

Written table⁠

A <table> written inside the element is its source, so crawlers and readers without script still see the rows. Header cells take options such as data-cw-field and data-cw-sortable.

Properties⁠

PropertyAttributeTypeDescription
borderlessborderlessbooleandefault falseRemoves the table's outer frame. The header's underline stays, drawn in the row divider's color and width, as do the row and cell dividers. See Lines and stripes.
captionPositioncaption-positionleading, trailingCaptionPositionCaptionPosition.Leading, .Trailingdefault CaptionPosition.Leading, from the style configurationThe caption's position in relation to the component. See Caption position.
columnsColumnDefinition[]default []Column definitions. If not provided, columns will be auto-generated from data (if possible). See Columns and rows.
densitydensity0, 1, 2, 3, 4DensityDensity.Standard, .Dense1, .Dense2, .Dense3, .Dense4default Density.Standard, from the style configurationHow tightly rows are spaced, from 0, the roomiest, to 4, the tightest. See Density.
gridLabelgrid-labelstringThe name a screen reader announces for the component. See Caption position.
itemsTItem[]default []Data source as an array of objects. Use this for client-side data. See Columns and rows.
itemsProviderGridItemsProvider<TItem>Async function to provide data. Use this for server-side operations (filtering, sorting, paging). Ignored while items has rows. See Large data sets.
localelocalestringThe BCP 47 locale, such as de-DE, the component writes numbers and dates in, and translates its built-in screen-reader text into where it has a translation. Empty means the application's configured locale (AppConfiguration.locale), or the browser's preferred language when it sets none.
overscanCountoverscan-countnumberdefault 3How many rows beyond the visible ones are fetched ahead of scrolling. Keep it small to avoid over-fetching from the server. See Large data sets.
showColumnDividersshow-column-dividersbooleandefault falseShows vertical dividers between columns, which helps tell columns apart when there are many. See Lines and stripes.
stackstacknever, always, autoTableStackTableStack.Never, .Always, .Autodefault TableStack.NeverHow the table fits a narrow width. always draws each row as a block, with every value beside its column's title; auto does so only while the table cannot show its content at the width it has, measured with its rows as rows.Has no effect while virtualize is set: virtualized rows share one height, and a row drawn as a block is as tall as its content. See Stack.
stripedstripedbooleandefault falseShades alternate rows. See Lines and stripes.
templateFieldsstring[]default []The fields whose cells show content the page supplies, through a child with slot="cell:{rowKey}:{field}", rather than their value. Every cell of such a field shows its slot, so the content can arrive after the row is drawn; rows-change names the rows drawn. {rowKey} is the row's value in the uniqueIdentifier field, which these fields need.A written <table> needs no list: any written cell with such a child shows it in place of its copy. See Cell content.
uniqueIdentifierunique-identifierstringThe name of a field whose value identifies a row. Optional, except when templateFields is set. Without it, the table cannot restore keyboard focus to the same row after a sort, filter or page change, so it drops focus. See Columns and rows, Cell content.
variantvariantfilled, standard, surface, tonalTableVariantTableVariant.Filled, .Standard, .Surface, .Tonaldefault TableVariant.Standard, from the style configurationThe component's color scheme. filled uses the primary container color for high emphasis, tonal the secondary container color for medium emphasis, surface the lowest surface container color, and standard no fill, taking the color of whatever the table sits on. Unset, it comes from the style configuration's defaultTableVariant, which ships as standard. See Variants.
virtualizevirtualizebooleandefault falseShows only the rows in view, for large datasets. Every row must be the same height. See Large data sets.
wrapCellswrap-cellsbooleandefault falseCells wrap their text rather than truncating it. Rows grow to fit, the row height becomes a minimum, columns size to their content and the table scrolls sideways when its content is wider than its space.Has no effect while virtualize is set, for the reason stack gives. See Wrapping text.

When items and itemsProvider are both set, items wins, and a warning is logged. A written table in the content takes precedence over items, also with a warning. Without virtualize, a provider is called once for every row.

Slots⁠

SlotDescription
(default)Caption content displayed above or below the table. Not rendered while a written <table> is the source; its <caption> is shown instead.

A child with slot name cell:{rowKey}:{field} fills one cell; see templateFields. A caption replaces grid-label as the accessible name.

Events⁠

EventTypeDetailDescription
sort-changeSortChangeEventSortChangeEventDetailDispatched when the sort state changes (a sortable column header is activated). It bubbles and is composed.
rows-changeRowsChangeEventRowsChangeEventDetailDispatched when the rows drawn change, naming each drawn row's key in order, so a page can supply cell-slot content for the drawn rows alone. It bubbles and is composed.
cell-activateCellActivateEventCellActivateEventDetail<TItem>Dispatched when a control rendered by ColumnDefinition.actionLabel/actionHref is activated — by pointer, Enter, or Space. It bubbles and is composed.

With items, a sort reorders a copy and leaves the array unchanged. With itemsProvider, the sort reaches the provider as sortDescriptors, and the provider must sort.

Props⁠

PropTypeDescription
borderlessbooleandefault falseRemoves the table's outer frame. The header's underline stays, drawn in the row divider's color and width, as do the row and cell dividers. See Lines and stripes.
captionPositionCaptionPositionTypeCaptionPosition.Leading, .Trailingdefault CaptionPosition.Leading, from the style configurationThe caption's position in relation to the component. See Caption position.
columnsColumnDefinition[]default []Column definitions. If not provided, columns will be auto-generated from data (if possible). See Columns and rows.
densityDensityTypeDensity.Standard, .Dense1, .Dense2, .Dense3, .Dense4default Density.Standard, from the style configurationHow tightly rows are spaced, from 0, the roomiest, to 4, the tightest. See Density.
gridLabelstringThe name a screen reader announces for the component. See Caption position.
itemsTItem[]default []Data source as an array of objects. Use this for client-side data. See Columns and rows.
itemsProviderGridItemsProvider<TItem>Async function to provide data. Use this for server-side operations (filtering, sorting, paging). Ignored while items has rows. See Large data sets.
localestringThe BCP 47 locale, such as de-DE, the component writes numbers and dates in, and translates its built-in screen-reader text into where it has a translation. Empty means the application's configured locale (AppConfiguration.locale), or the browser's preferred language when it sets none.
overscanCountnumberdefault 3How many rows beyond the visible ones are fetched ahead of scrolling. Keep it small to avoid over-fetching from the server. See Large data sets.
showColumnDividersbooleandefault falseShows vertical dividers between columns, which helps tell columns apart when there are many. See Lines and stripes.
stackTableStackTypeTableStack.Never, .Always, .Autodefault TableStack.NeverHow the table fits a narrow width. always draws each row as a block, with every value beside its column's title; auto does so only while the table cannot show its content at the width it has, measured with its rows as rows.Has no effect while virtualize is set: virtualized rows share one height, and a row drawn as a block is as tall as its content. See Stack.
stripedbooleandefault falseShades alternate rows. See Lines and stripes.
templateFieldsstring[]default []The fields whose cells show content the page supplies, through a child with slot="cell:{rowKey}:{field}", rather than their value. Every cell of such a field shows its slot, so the content can arrive after the row is drawn; rows-change names the rows drawn. {rowKey} is the row's value in the uniqueIdentifier field, which these fields need.A written <table> needs no list: any written cell with such a child shows it in place of its copy. See Cell content.
uniqueIdentifierstringThe name of a field whose value identifies a row. Optional, except when templateFields is set. Without it, the table cannot restore keyboard focus to the same row after a sort, filter or page change, so it drops focus. See Columns and rows, Cell content.
variantTableVariantTypeTableVariant.Filled, .Standard, .Surface, .Tonaldefault TableVariant.Standard, from the style configurationThe component's color scheme. filled uses the primary container color for high emphasis, tonal the secondary container color for medium emphasis, surface the lowest surface container color, and standard no fill, taking the color of whatever the table sits on. Unset, it comes from the style configuration's defaultTableVariant, which ships as standard. See Variants.
virtualizebooleandefault falseShows only the rows in view, for large datasets. Every row must be the same height. See Large data sets.
wrapCellsbooleandefault falseCells wrap their text rather than truncating it. Rows grow to fit, the row height becomes a minimum, columns size to their content and the table scrolls sideways when its content is wider than its space.Has no effect while virtualize is set, for the reason stack gives. See Wrapping text.

When items and itemsProvider are both set, items wins, and a warning is logged. A written table in the content takes precedence over items, also with a warning. Without virtualize, a provider is called once for every row.

Children⁠

ChildDescription
childrenCaption content displayed above or below the table. Not rendered while a written <table> is the source; its <caption> is shown instead.

A child with slot name cell:{rowKey}:{field} fills one cell; see templateFields. A caption replaces grid-label as the accessible name.

Events⁠

PropArgumentDescription
onSortChangeSortChangeEventDispatched when the sort state changes (a sortable column header is activated). It bubbles and is composed. Its detail carries column, direction, and sortDescriptors.
onRowsChangeRowsChangeEventDispatched when the rows drawn change, naming each drawn row's key in order, so a page can supply cell-slot content for the drawn rows alone. It bubbles and is composed. Its detail carries keys.
onCellActivateCellActivateEvent<TItem>Dispatched when a control rendered by ColumnDefinition.actionLabel/actionHref is activated — by pointer, Enter, or Space. It bubbles and is composed. Its detail carries item, rowIndex, field, columnIndex, value, and originalEvent.

With items, a sort reorders a copy and leaves the array unchanged. With itemsProvider, the sort reaches the provider as sortDescriptors, and the provider must sort.

Parameters⁠

ParameterTypeDescription
Borderlessbool?default falseRemoves the table's outer frame. The header's underline stays, drawn in the row divider's color and width, as do the row and cell dividers. See Lines and stripes.
CaptionPositionCaptionPosition?CaptionPosition.Leading, .Trailingdefault CaptionPosition.Leading, from the style configurationThe caption's position in relation to the component. See Caption position.
ColumnsIEnumerable<ColumnDefinition>?default []Column definitions. If not provided, columns will be auto-generated from data (if possible). See Columns and rows.
DensityDensity?Density.Standard, .Dense1, .Dense2, .Dense3, .Dense4default Density.Standard, from the style configurationHow tightly rows are spaced, from 0, the roomiest, to 4, the tightest. See Density.
GridLabelstring?The name a screen reader announces for the component. See Caption position.
ItemsIEnumerable<TItem>?default []Data source as an array of objects. Use this for client-side data. See Columns and rows.
ItemsProviderGridItemsProvider<TItem>?Async function to provide data. Use this for server-side operations (filtering, sorting, paging). Ignored while Items has rows. See Large data sets.
Localestring?The BCP 47 locale, such as de-DE, the component writes numbers and dates in, and translates its built-in screen-reader text into where it has a translation. Empty means the application's configured locale (AppConfiguration.locale), or the browser's preferred language when it sets none.
OverscanCountint?default 3How many rows beyond the visible ones are fetched ahead of scrolling. Keep it small to avoid over-fetching from the server. See Large data sets.
ShowColumnDividersbool?default falseShows vertical dividers between columns, which helps tell columns apart when there are many. See Lines and stripes.
StackTableStack?TableStack.Never, .Always, .Autodefault TableStack.NeverHow the table fits a narrow width. always draws each row as a block, with every value beside its column's title; auto does so only while the table cannot show its content at the width it has, measured with its rows as rows.Has no effect while Virtualize is set: virtualized rows share one height, and a row drawn as a block is as tall as its content. See Stack.
Stripedbool?default falseShades alternate rows. See Lines and stripes.
TemplateFieldsIEnumerable<string>?default []The fields whose cells show content the page supplies, through a child with slot="cell:{rowKey}:{field}", rather than their value. Every cell of such a field shows its slot, so the content can arrive after the row is drawn; rows-change names the rows drawn. {rowKey} is the row's value in the UniqueIdentifier field, which these fields need.A written <table> needs no list: any written cell with such a child shows it in place of its copy. See Cell content.
UniqueIdentifierstring?The name of a field whose value identifies a row. Optional, except when TemplateFields is set. Without it, the table cannot restore keyboard focus to the same row after a sort, filter or page change, so it drops focus. See Columns and rows, Cell content.
VariantTableVariant?TableVariant.Filled, .Standard, .Surface, .Tonaldefault TableVariant.Standard, from the style configurationThe component's color scheme. filled uses the primary container color for high emphasis, tonal the secondary container color for medium emphasis, surface the lowest surface container color, and standard no fill, taking the color of whatever the table sits on. Unset, it comes from the style configuration's defaultTableVariant, which ships as standard. See Variants.
Virtualizebool?default falseShows only the rows in view, for large datasets. Every row must be the same height. See Large data sets.
WrapCellsbool?default falseCells wrap their text rather than truncating it. Rows grow to fit, the row height becomes a minimum, columns size to their content and the table scrolls sideways when its content is wider than its space.Has no effect while Virtualize is set, for the reason Stack gives. See Wrapping text.

When items and itemsProvider are both set, items wins, and a warning is logged. A written table in the content takes precedence over items, also with a warning. Without virtualize, a provider is called once for every row.

Render fragments⁠

ParameterTypeDescription
ChildContentRenderFragment?Caption content displayed above or below the table. Not rendered while a written <table> is the source; its <caption> is shown instead.

A child with slot name cell:{rowKey}:{field} fills one cell; see templateFields. A caption replaces grid-label as the accessible name.

Event callbacks⁠

ParameterTypeDescription
OnSortChangeEventCallback<SortChangeEventArgs>Dispatched when the sort state changes (a sortable column header is activated). It bubbles and is composed. Its detail carries column, direction, and sortDescriptors.
OnRowsChangeEventCallback<RowsChangeEventArgs>Dispatched when the rows drawn change, naming each drawn row's key in order, so a page can supply cell-slot content for the drawn rows alone. It bubbles and is composed. Its detail carries keys.
OnCellActivateEventCallback<CellActivateEventArgs<TItem>>Dispatched when a control rendered by ColumnDefinition.actionLabel/actionHref is activated — by pointer, Enter, or Space. It bubbles and is composed. Its detail carries item, rowIndex, field, columnIndex, value, and originalEvent.

With items, a sort reorders a copy and leaves the array unchanged. With itemsProvider, the sort reaches the provider as sortDescriptors, and the provider must sort.

Inputs⁠

InputTypeDescription
borderlessbooleandefault falseRemoves the table's outer frame. The header's underline stays, drawn in the row divider's color and width, as do the row and cell dividers. See Lines and stripes.
captionPositionCaptionPositionTypeCaptionPosition.Leading, .Trailingdefault CaptionPosition.Leading, from the style configurationThe caption's position in relation to the component. See Caption position.
columnsColumnDefinition[]default []Column definitions. If not provided, columns will be auto-generated from data (if possible). See Columns and rows.
densityDensityTypeDensity.Standard, .Dense1, .Dense2, .Dense3, .Dense4default Density.Standard, from the style configurationHow tightly rows are spaced, from 0, the roomiest, to 4, the tightest. See Density.
gridLabelstringThe name a screen reader announces for the component. See Caption position.
itemsTItem[]default []Data source as an array of objects. Use this for client-side data. See Columns and rows.
itemsProviderGridItemsProvider<TItem>Async function to provide data. Use this for server-side operations (filtering, sorting, paging). Ignored while items has rows. See Large data sets.
localestringThe BCP 47 locale, such as de-DE, the component writes numbers and dates in, and translates its built-in screen-reader text into where it has a translation. Empty means the application's configured locale (AppConfiguration.locale), or the browser's preferred language when it sets none.
overscanCountnumberdefault 3How many rows beyond the visible ones are fetched ahead of scrolling. Keep it small to avoid over-fetching from the server. See Large data sets.
showColumnDividersbooleandefault falseShows vertical dividers between columns, which helps tell columns apart when there are many. See Lines and stripes.
stackTableStackTypeTableStack.Never, .Always, .Autodefault TableStack.NeverHow the table fits a narrow width. always draws each row as a block, with every value beside its column's title; auto does so only while the table cannot show its content at the width it has, measured with its rows as rows.Has no effect while virtualize is set: virtualized rows share one height, and a row drawn as a block is as tall as its content. See Stack.
stripedbooleandefault falseShades alternate rows. See Lines and stripes.
templateFieldsstring[]default []The fields whose cells show content the page supplies, through a child with slot="cell:{rowKey}:{field}", rather than their value. Every cell of such a field shows its slot, so the content can arrive after the row is drawn; rows-change names the rows drawn. {rowKey} is the row's value in the uniqueIdentifier field, which these fields need.A written <table> needs no list: any written cell with such a child shows it in place of its copy. See Cell content.
uniqueIdentifierstringThe name of a field whose value identifies a row. Optional, except when templateFields is set. Without it, the table cannot restore keyboard focus to the same row after a sort, filter or page change, so it drops focus. See Columns and rows, Cell content.
variantTableVariantTypeTableVariant.Filled, .Standard, .Surface, .Tonaldefault TableVariant.Standard, from the style configurationThe component's color scheme. filled uses the primary container color for high emphasis, tonal the secondary container color for medium emphasis, surface the lowest surface container color, and standard no fill, taking the color of whatever the table sits on. Unset, it comes from the style configuration's defaultTableVariant, which ships as standard. See Variants.
virtualizebooleandefault falseShows only the rows in view, for large datasets. Every row must be the same height. See Large data sets.
wrapCellsbooleandefault falseCells wrap their text rather than truncating it. Rows grow to fit, the row height becomes a minimum, columns size to their content and the table scrolls sideways when its content is wider than its space.Has no effect while virtualize is set, for the reason stack gives. See Wrapping text.

When items and itemsProvider are both set, items wins, and a warning is logged. A written table in the content takes precedence over items, also with a warning. Without virtualize, a provider is called once for every row.

Content projection⁠

ContentDescription
content with no slotCaption content displayed above or below the table. Not rendered while a written <table> is the source; its <caption> is shown instead.

A child with slot name cell:{rowKey}:{field} fills one cell; see templateFields. A caption replaces grid-label as the accessible name.

Outputs⁠

OutputEventDescription
cwSortChangeSortChangeEventDispatched when the sort state changes (a sortable column header is activated). It bubbles and is composed. Its detail carries column, direction, and sortDescriptors.
cwRowsChangeRowsChangeEventDispatched when the rows drawn change, naming each drawn row's key in order, so a page can supply cell-slot content for the drawn rows alone. It bubbles and is composed. Its detail carries keys.
cwCellActivateCellActivateEventDispatched when a control rendered by ColumnDefinition.actionLabel/actionHref is activated — by pointer, Enter, or Space. It bubbles and is composed. Its detail carries item, rowIndex, field, columnIndex, value, and originalEvent.

With items, a sort reorders a copy and leaves the array unchanged. With itemsProvider, the sort reaches the provider as sortDescriptors, and the provider must sort.

Props⁠

PropTypeDescription
borderlessbooleandefault falseRemoves the table's outer frame. The header's underline stays, drawn in the row divider's color and width, as do the row and cell dividers. See Lines and stripes.
captionPositionCaptionPositionTypeCaptionPosition.Leading, .Trailingdefault CaptionPosition.Leading, from the style configurationThe caption's position in relation to the component. See Caption position.
columnsColumnDefinition[]default []Column definitions. If not provided, columns will be auto-generated from data (if possible). See Columns and rows.
densityDensityTypeDensity.Standard, .Dense1, .Dense2, .Dense3, .Dense4default Density.Standard, from the style configurationHow tightly rows are spaced, from 0, the roomiest, to 4, the tightest. See Density.
gridLabelstringThe name a screen reader announces for the component. See Caption position.
itemsTItem[]default []Data source as an array of objects. Use this for client-side data. See Columns and rows.
itemsProviderGridItemsProvider<TItem>Async function to provide data. Use this for server-side operations (filtering, sorting, paging). Ignored while items has rows. See Large data sets.
localestringThe BCP 47 locale, such as de-DE, the component writes numbers and dates in, and translates its built-in screen-reader text into where it has a translation. Empty means the application's configured locale (AppConfiguration.locale), or the browser's preferred language when it sets none.
overscanCountnumberdefault 3How many rows beyond the visible ones are fetched ahead of scrolling. Keep it small to avoid over-fetching from the server. See Large data sets.
showColumnDividersbooleandefault falseShows vertical dividers between columns, which helps tell columns apart when there are many. See Lines and stripes.
stackTableStackTypeTableStack.Never, .Always, .Autodefault TableStack.NeverHow the table fits a narrow width. always draws each row as a block, with every value beside its column's title; auto does so only while the table cannot show its content at the width it has, measured with its rows as rows.Has no effect while virtualize is set: virtualized rows share one height, and a row drawn as a block is as tall as its content. See Stack.
stripedbooleandefault falseShades alternate rows. See Lines and stripes.
templateFieldsstring[]default []The fields whose cells show content the page supplies, through a child with slot="cell:{rowKey}:{field}", rather than their value. Every cell of such a field shows its slot, so the content can arrive after the row is drawn; rows-change names the rows drawn. {rowKey} is the row's value in the uniqueIdentifier field, which these fields need.A written <table> needs no list: any written cell with such a child shows it in place of its copy. See Cell content.
uniqueIdentifierstringThe name of a field whose value identifies a row. Optional, except when templateFields is set. Without it, the table cannot restore keyboard focus to the same row after a sort, filter or page change, so it drops focus. See Columns and rows, Cell content.
variantTableVariantTypeTableVariant.Filled, .Standard, .Surface, .Tonaldefault TableVariant.Standard, from the style configurationThe component's color scheme. filled uses the primary container color for high emphasis, tonal the secondary container color for medium emphasis, surface the lowest surface container color, and standard no fill, taking the color of whatever the table sits on. Unset, it comes from the style configuration's defaultTableVariant, which ships as standard. See Variants.
virtualizebooleandefault falseShows only the rows in view, for large datasets. Every row must be the same height. See Large data sets.
wrapCellsbooleandefault falseCells wrap their text rather than truncating it. Rows grow to fit, the row height becomes a minimum, columns size to their content and the table scrolls sideways when its content is wider than its space.Has no effect while virtualize is set, for the reason stack gives. See Wrapping text.

When items and itemsProvider are both set, items wins, and a warning is logged. A written table in the content takes precedence over items, also with a warning. Without virtualize, a provider is called once for every row.

Slots⁠

SlotDescription
defaultCaption content displayed above or below the table. Not rendered while a written <table> is the source; its <caption> is shown instead.

A child with slot name cell:{rowKey}:{field} fills one cell; see templateFields. A caption replaces grid-label as the accessible name.

Events⁠

EventTypeDescription
sortChangeSortChangeEventDispatched when the sort state changes (a sortable column header is activated). It bubbles and is composed. Its detail carries column, direction, and sortDescriptors.
rowsChangeRowsChangeEventDispatched when the rows drawn change, naming each drawn row's key in order, so a page can supply cell-slot content for the drawn rows alone. It bubbles and is composed. Its detail carries keys.
cellActivateCellActivateEventDispatched when a control rendered by ColumnDefinition.actionLabel/actionHref is activated — by pointer, Enter, or Space. It bubbles and is composed. Its detail carries item, rowIndex, field, columnIndex, value, and originalEvent.

With items, a sort reorders a copy and leaves the array unchanged. With itemsProvider, the sort reaches the provider as sortDescriptors, and the provider must sort.

Props⁠

PropTypeDescription
borderlessbooleandefault falseRemoves the table's outer frame. The header's underline stays, drawn in the row divider's color and width, as do the row and cell dividers. See Lines and stripes.
captionPositionCaptionPositionTypeCaptionPosition.Leading, .Trailingdefault CaptionPosition.Leading, from the style configurationThe caption's position in relation to the component. See Caption position.
columnsColumnDefinition[]default []Column definitions. If not provided, columns will be auto-generated from data (if possible). See Columns and rows.
densityDensityTypeDensity.Standard, .Dense1, .Dense2, .Dense3, .Dense4default Density.Standard, from the style configurationHow tightly rows are spaced, from 0, the roomiest, to 4, the tightest. See Density.
gridLabelstringThe name a screen reader announces for the component. See Caption position.
itemsTItem[]default []Data source as an array of objects. Use this for client-side data. See Columns and rows.
itemsProviderGridItemsProvider<TItem>Async function to provide data. Use this for server-side operations (filtering, sorting, paging). Ignored while items has rows. See Large data sets.
localestringThe BCP 47 locale, such as de-DE, the component writes numbers and dates in, and translates its built-in screen-reader text into where it has a translation. Empty means the application's configured locale (AppConfiguration.locale), or the browser's preferred language when it sets none.
overscanCountnumberdefault 3How many rows beyond the visible ones are fetched ahead of scrolling. Keep it small to avoid over-fetching from the server. See Large data sets.
showColumnDividersbooleandefault falseShows vertical dividers between columns, which helps tell columns apart when there are many. See Lines and stripes.
stackTableStackTypeTableStack.Never, .Always, .Autodefault TableStack.NeverHow the table fits a narrow width. always draws each row as a block, with every value beside its column's title; auto does so only while the table cannot show its content at the width it has, measured with its rows as rows.Has no effect while virtualize is set: virtualized rows share one height, and a row drawn as a block is as tall as its content. See Stack.
stripedbooleandefault falseShades alternate rows. See Lines and stripes.
templateFieldsstring[]default []The fields whose cells show content the page supplies, through a child with slot="cell:{rowKey}:{field}", rather than their value. Every cell of such a field shows its slot, so the content can arrive after the row is drawn; rows-change names the rows drawn. {rowKey} is the row's value in the uniqueIdentifier field, which these fields need.A written <table> needs no list: any written cell with such a child shows it in place of its copy. See Cell content.
uniqueIdentifierstringThe name of a field whose value identifies a row. Optional, except when templateFields is set. Without it, the table cannot restore keyboard focus to the same row after a sort, filter or page change, so it drops focus. See Columns and rows, Cell content.
variantTableVariantTypeTableVariant.Filled, .Standard, .Surface, .Tonaldefault TableVariant.Standard, from the style configurationThe component's color scheme. filled uses the primary container color for high emphasis, tonal the secondary container color for medium emphasis, surface the lowest surface container color, and standard no fill, taking the color of whatever the table sits on. Unset, it comes from the style configuration's defaultTableVariant, which ships as standard. See Variants.
virtualizebooleandefault falseShows only the rows in view, for large datasets. Every row must be the same height. See Large data sets.
wrapCellsbooleandefault falseCells wrap their text rather than truncating it. Rows grow to fit, the row height becomes a minimum, columns size to their content and the table scrolls sideways when its content is wider than its space.Has no effect while virtualize is set, for the reason stack gives. See Wrapping text.

When items and itemsProvider are both set, items wins, and a warning is logged. A written table in the content takes precedence over items, also with a warning. Without virtualize, a provider is called once for every row.

Snippets⁠

SnippetDescription
childrenCaption content displayed above or below the table. Not rendered while a written <table> is the source; its <caption> is shown instead.

A child with slot name cell:{rowKey}:{field} fills one cell; see templateFields. A caption replaces grid-label as the accessible name.

Events⁠

PropTypeDescription
onSortChangeSortChangeEventDispatched when the sort state changes (a sortable column header is activated). It bubbles and is composed. Its detail carries column, direction, and sortDescriptors.
onRowsChangeRowsChangeEventDispatched when the rows drawn change, naming each drawn row's key in order, so a page can supply cell-slot content for the drawn rows alone. It bubbles and is composed. Its detail carries keys.
onCellActivateCellActivateEventDispatched when a control rendered by ColumnDefinition.actionLabel/actionHref is activated — by pointer, Enter, or Space. It bubbles and is composed. Its detail carries item, rowIndex, field, columnIndex, value, and originalEvent.

With items, a sort reorders a copy and leaves the array unchanged. With itemsProvider, the sort reaches the provider as sortDescriptors, and the provider must sort.

Each attribute value has its own tokens; a cell shows its token's value, and selecting the cell shows the token's fallback path, with each name and value ready to copy. For example, the standard column's error-color row is --ct-table-standard-error-color, which defaults to --ct-sys-color-error.

Variant⁠

Namestandardsurfacefilledtonal
error-color--ct-sys-color-error --ct-table-standard-error-color--ct-sys-color-error --ct-table-surface-error-color--ct-sys-color-error --ct-table-filled-error-color--ct-sys-color-error --ct-table-tonal-error-color
error-container-color--ct-sys-color-error-container --ct-table-standard-error-container-color--ct-sys-color-error-container --ct-table-surface-error-container-color--ct-sys-color-error-container --ct-table-filled-error-container-color--ct-sys-color-error-container --ct-table-tonal-error-container-color
header-container-colortransparent --ct-table-standard-header-container-color--ct-sys-color-surface-container-lowest --ct-table-surface-header-container-color--ct-sys-color-primary-container --ct-table-filled-header-container-color--ct-sys-color-secondary-container --ct-table-tonal-header-container-color
header-headline-color--ct-sys-color-on-surface --ct-table-standard-header-headline-color--ct-sys-color-on-surface --ct-table-surface-header-headline-color--ct-sys-color-on-primary-container --ct-table-filled-header-headline-color--ct-sys-color-on-secondary-container --ct-table-tonal-header-headline-color
header-hover-headline-colorsame as row-item-label-text-color --ct-table-standard-header-hover-headline-colorsame as row-item-label-text-color --ct-table-surface-header-hover-headline-colorsame as row-item-label-text-color --ct-table-filled-header-hover-headline-colorsame as row-item-label-text-color --ct-table-tonal-header-hover-headline-color
header-hover-sorting-icon-button-colorsame as row-item-label-text-color --ct-table-standard-header-hover-sorting-icon-button-colorsame as row-item-label-text-color --ct-table-surface-header-hover-sorting-icon-button-colorsame as row-item-label-text-color --ct-table-filled-header-hover-sorting-icon-button-colorsame as row-item-label-text-color --ct-table-tonal-header-hover-sorting-icon-button-color
on-error-container-color--ct-sys-color-on-error-container --ct-table-standard-on-error-container-color--ct-sys-color-on-error-container --ct-table-surface-on-error-container-color--ct-sys-color-on-error-container --ct-table-filled-on-error-container-color--ct-sys-color-on-error-container --ct-table-tonal-on-error-container-color
outline-color--ct-sys-color-outline-variant --ct-table-standard-outline-color--ct-sys-color-outline-variant --ct-table-surface-outline-color--ct-sys-color-primary --ct-table-filled-outline-color--ct-sys-color-secondary --ct-table-tonal-outline-color
row-item-container-colortransparent --ct-table-standard-row-item-container-color--ct-sys-color-surface-container-lowest --ct-table-surface-row-item-container-color--ct-sys-color-primary-container --ct-table-filled-row-item-container-color--ct-sys-color-secondary-container --ct-table-tonal-row-item-container-color
row-item-hover-state-layer-color--ct-sys-color-on-surface --ct-table-standard-row-item-hover-state-layer-color--ct-sys-color-on-surface --ct-table-surface-row-item-hover-state-layer-color--ct-sys-color-on-primary-container --ct-table-filled-row-item-hover-state-layer-color--ct-sys-color-on-secondary-container --ct-table-tonal-row-item-hover-state-layer-color
row-item-hover-state-layer-opacity--ct-sys-state-hover-state-layer-opacity --ct-table-standard-row-item-hover-state-layer-opacity--ct-sys-state-hover-state-layer-opacity --ct-table-surface-row-item-hover-state-layer-opacity--ct-sys-state-hover-state-layer-opacity --ct-table-filled-row-item-hover-state-layer-opacity--ct-sys-state-hover-state-layer-opacity --ct-table-tonal-row-item-hover-state-layer-opacity
row-item-label-text-color--ct-sys-color-on-surface --ct-table-standard-row-item-label-text-color--ct-sys-color-on-surface --ct-table-surface-row-item-label-text-color--ct-sys-color-on-primary-container --ct-table-filled-row-item-label-text-color--ct-sys-color-on-secondary-container --ct-table-tonal-row-item-label-text-color
row-item-outline-color--ct-sys-color-outline-variant --ct-table-standard-row-item-outline-color--ct-sys-color-outline-variant --ct-table-surface-row-item-outline-color--ct-sys-color-primary --ct-table-filled-row-item-outline-color--ct-sys-color-secondary --ct-table-tonal-row-item-outline-color
row-item-selected-hover-state-layer-colorsame as row-item-hover-state-layer-color --ct-table-standard-row-item-selected-hover-state-layer-colorsame as row-item-hover-state-layer-color --ct-table-surface-row-item-selected-hover-state-layer-colorsame as row-item-hover-state-layer-color --ct-table-filled-row-item-selected-hover-state-layer-colorsame as row-item-hover-state-layer-color --ct-table-tonal-row-item-selected-hover-state-layer-color
row-item-selected-hover-state-layer-opacity--ct-sys-state-hover-state-layer-opacity --ct-table-standard-row-item-selected-hover-state-layer-opacity--ct-sys-state-hover-state-layer-opacity --ct-table-surface-row-item-selected-hover-state-layer-opacity--ct-sys-state-hover-state-layer-opacity --ct-table-filled-row-item-selected-hover-state-layer-opacity--ct-sys-state-hover-state-layer-opacity --ct-table-tonal-row-item-selected-hover-state-layer-opacity
scrollbar-color--ct-sys-color-outline-variant --ct-table-standard-scrollbar-color--ct-sys-color-outline-variant --ct-table-surface-scrollbar-color--ct-sys-color-primary --ct-table-filled-scrollbar-color--ct-sys-color-secondary --ct-table-tonal-scrollbar-color
scrollbar-thumb-color--ct-sys-color-on-surface-variant --ct-table-standard-scrollbar-thumb-color--ct-sys-color-on-surface-variant --ct-table-surface-scrollbar-thumb-color--ct-sys-color-on-primary-container --ct-table-filled-scrollbar-thumb-color--ct-sys-color-on-secondary-container --ct-table-tonal-scrollbar-thumb-color
scrollbar-thumb-hover-color--ct-sys-color-on-surface --ct-table-standard-scrollbar-thumb-hover-color--ct-sys-color-on-surface --ct-table-surface-scrollbar-thumb-hover-color--ct-sys-color-on-primary --ct-table-filled-scrollbar-thumb-hover-color--ct-sys-color-on-secondary --ct-table-tonal-scrollbar-thumb-hover-color

Other tokens⁠

These stay the same whatever the variant.

TokenValue
--ct-table-caption-text-color--ct-sys-color-on-surface --ct-table-caption-text-color
--ct-table-caption-text-font--ct-sys-typescale-title-medium-font --ct-table-caption-text-font
--ct-table-caption-text-line-height--ct-sys-typescale-title-medium-line-height --ct-table-caption-text-line-height
--ct-table-caption-text-size--ct-sys-typescale-title-medium-size --ct-table-caption-text-size
--ct-table-caption-text-tracking--ct-sys-typescale-title-medium-tracking --ct-table-caption-text-tracking
--ct-table-caption-text-weight--ct-sys-typescale-title-medium-weight --ct-table-caption-text-weight
--ct-table-footer-container-dense-1-height48px --ct-table-footer-container-dense-1-height
--ct-table-footer-container-dense-2-height44px --ct-table-footer-container-dense-2-height
--ct-table-footer-container-dense-3-height40px --ct-table-footer-container-dense-3-height
--ct-table-footer-container-dense-4-height36px --ct-table-footer-container-dense-4-height
--ct-table-footer-container-standard-height52px --ct-table-footer-container-standard-height
--ct-table-header-container-dense-1-height52px --ct-table-header-container-dense-1-height
--ct-table-header-container-dense-2-height48px --ct-table-header-container-dense-2-height
--ct-table-header-container-dense-3-height44px --ct-table-header-container-dense-3-height
--ct-table-header-container-dense-4-height40px --ct-table-header-container-dense-4-height
--ct-table-header-container-standard-height56px --ct-table-header-container-standard-height
--ct-table-header-headline-font--ct-sys-typescale-title-small-font --ct-table-header-headline-font
--ct-table-header-headline-size--ct-sys-typescale-title-small-size --ct-table-header-headline-size
--ct-table-header-headline-weight--ct-sys-typescale-title-small-weight --ct-table-header-headline-weight
--ct-table-header-sticky-offset0px --ct-table-header-sticky-offset
--ct-table-outline-width1px --ct-table-outline-width
--ct-table-row-item-label-text-font--ct-sys-typescale-body-medium-font --ct-table-row-item-label-text-font
--ct-table-row-item-label-text-size--ct-sys-typescale-body-medium-size --ct-table-row-item-label-text-size
--ct-table-row-item-label-text-weight--ct-sys-typescale-body-medium-weight --ct-table-row-item-label-text-weight
--ct-table-row-item-outline-width1px --ct-table-row-item-outline-width
--ct-table-row-layers-container-dense-1-height48px --ct-table-row-layers-container-dense-1-height
--ct-table-row-layers-container-dense-2-height44px --ct-table-row-layers-container-dense-2-height
--ct-table-row-layers-container-dense-3-height40px --ct-table-row-layers-container-dense-3-height
--ct-table-row-layers-container-dense-4-height36px --ct-table-row-layers-container-dense-4-height
--ct-table-row-layers-container-standard-height52px --ct-table-row-layers-container-standard-height

Names and states⁠

A screen reader announces a data table as a table with a header row. It follows the WAI-ARIA Authoring Practices: Table pattern, a static table with no cell navigation.

  • The caption is the first source of the name. Without one, grid-label names it, and a written table falls back to its own aria-label.
  • An aria-label set on the data table has no effect. Give the table a caption or grid-label.
  • A screen reader announces the row and column counts, and reads each header cell as a column header.
  • A sortable header is a button named for its column, and a screen reader announces its sort state. The sort arrow itself is not announced.
  • A sort is announced in a live region, such as "Sorted by Customer, ascending". Loads announce "Loading data", "Loaded 3 rows", and "No data to display".
  • Choose a data grid when readers must move between cells by keyboard or select rows. A data table has no cell roles.

Keyboard and focus⁠

Key Action
Tab Moves to the next control in the table, such as a sort button, a cell action, or your own content.
Shift+Tab Moves to the previous control.
Enter Activates the focused sort button or cell action.
Space Activates the focused sort button or cell action.

These are the keys of an ordinary button or link; the table adds no keys of its own.

  • A sort button shows a focus ring on keyboard focus.
  • Cells are not focusable. Only interactive content inside cells is in the Tab order.
  • A table that scrolls takes one Tab stop, announced as a group named by grid-label or "Scrollable table", so keyboard users can scroll it. A table that does not scroll takes no Tab stop.

Pointer and touch⁠

  • The sort arrow is always visible, so touch users can see that a header is interactive.
  • Keep controls you place in cells, such as buttons from cell content, at least 24 by 24 pixels, the minimum in WCAG 2.5.8.

Display settings⁠

  • Forced colors: row dividers stay visible and take a system color.
  • Reduced motion: the table has no animation, so the setting changes nothing.
  • Right-to-left: text columns start at the inline edge, which is the right edge in a right-to-left layout.

Standards⁠

With its defaults, a data table meets four WCAG 2.2 success criteria:

  • 1.3.1 Info and Relationships (A)
  • 2.1.1 Keyboard (A)
  • 2.4.7 Focus Visible (AA)
  • 4.1.2 Name, Role, Value (A)

Three more depend on how you use it:

  • 1.1.1 Non-text Content (A): give every image in a cell a text alternative, or an empty alt when it is decorative.
  • 2.4.6 Headings and Labels (AA): give the table a caption or grid-label that describes its content, and keep column titles short.
  • 2.5.8 Target Size (Minimum) (AA): keep every control you place in a cell at least 24 by 24 pixels.