The ledger table: a real table element that sorts, filters, groups, selects and edits, with frozen columns, a sticky header, opt-in virtual rows and a stacked layout on narrow screens. Focus lands on the row, not the cell. Reach for it for records a person reads, and for sui-data-grid when the data is too large to read.
columns and rows are properties. A money column is aligned to the end in tabular numerals, and a column with render draws its own cell, here a link and a status pill.
<sui-data-table footer-meta="6 of 143 invoices"></sui-data-table>
A sortable header cycles ascending, descending and off, and carries aria-sort as it goes. With sort-mode multiple a Shift+click adds a term and the headers number themselves. By default only the sorted column draws its arrow and the rest show theirs on hover; sort-indicator all, as here, draws every glyph at rest.
<sui-data-table id="dt-sort" sort-mode="multiple" sort-indicator="all" footer-meta="Sorted by status, then amount."></sui-data-table>
filter-display row puts a box under each header that declares filter, and the search box in the header slot writes global-filter, which searches every visible column. A filter that leaves no rows shows empty-filter-message; empty-message is for no rows at all. filter-display menu gives a funnel button with match modes.
selection-mode checkbox adds the box column and a select-all box. While a row is selected, the selection-actions slot takes the top bar at its height, so the rows never move; give it role="toolbar", a name and the count in words. Select-all stores "everything except these", so it survives the next page.
row-detail adds the expander column and draws a panel under an open row. group-by puts a subheader over each group with the whole group's count and whatever group-summary returns. Rows are grouped first, sorted within the group (amount descending here) and paged last; a group past the page repeats its subheader.
row-tree maps each row key to its level and whether it is a branch, and the table becomes a treegrid: the first column indents and carries the twisty; a closed branch hides the rows under it. Hand it the whole outline in document order; expanded-rows names the open branches and suiExpandedRowsChange reports each turn.
<sui-data-table id="dt-tree" aria-label="Chart of accounts"></sui-data-table>
A column with pin start or pin end stays put while the rest scrolls sideways, and view-height keeps the header stuck to the top of the rows. gridlines draws the vertical rules.
A resizable column has a drag handle on its end edge, reorderable-columns lets a header be dragged to a new place, and show-column-toggle adds the columns button. All three write one layout object.
<sui-data-table id="dt-layout" reorderable-columns show-column-toggle footer-meta="Drag a header to move it, drag its edge to resize it."></sui-data-table>
edit-mode cell opens one editor on double click or F2; Enter commits, Escape cancels. A column names its control with an editor, and a validator can refuse it: clear the customer and the message stays until it passes. The table never writes the record: suiCellEdit reports it and the host hands back fresh rows.
Double click a customer or an amount.
<div style="display:grid;gap:10px">
<code id="dt-edit-log" style="font:460 12px/1.6 var(--font-sans);color:var(--ink-muted)">Double click a customer or an amount.</code>
<sui-data-table id="dt-edit" edit-mode="cell"></sui-data-table>
</div>
virtual mounts only the rows near the viewport and two spacer rows for the rest, so five thousand invoices cost a screenful of DOM. Every line is virtual-row-height tall, which is the price of the window.
<sui-data-table id="dt-virtual" virtual virtual-row-height="36" view-height="300px" density="compact" footer-meta="5,000 invoices, a screenful in the document."></sui-data-table>
footer-totals takes one string per column key, so a reordered or hidden column keeps its number. A sui-paginator in the footer slot is wired up: the table keeps its total in step with the filter and takes the page from it. At one page the paginator hides, and takes the footer bar with it when it is all the bar holds.
<sui-data-table id="dt-totals" aria-label="Invoices with totals" footer-meta="Filtered to the last 90 days.">
<sui-paginator slot="footer" page="1" page-size="4" hide-page-size></sui-paginator>
</sui-data-table>
bare drops the table's own border and corners, so the card is the one frame. A flush card hands its 20px inset (--sui-card-pad-x) to the table's first and last columns and its bars, so they start on the heading's line.
<sui-card heading="Invoices" flush>
<sui-button slot="header-end" variant="link" size="sm" label="View all"></sui-button>
<sui-data-table id="dt-card" bare aria-label="Invoices" footer-meta="6 of 143 invoices"></sui-data-table>
</sui-card>
Under breakpoint pixels of frame width each row becomes a card and every cell takes its column heading as a label. Narrow the window, or drag the splitter, to watch it turn over.
loading draws four shimmer rows, marks the body aria-busy and says Loading to a reader. With no rows the table shows empty-message and the empty-action slot, which offers the next step rather than an apology; the empty slot replaces the sentence itself, as on the grid, list and tree. The message carries role status.
<div style="display:grid;gap:16px">
<sui-data-table id="dt-load" loading></sui-data-table>
<sui-data-table id="dt-empty" empty-message="No open bills."><a slot="empty-action" href="#">Enter a bill to get started</a></sui-data-table>
<sui-data-table id="dt-empty-slot"><span slot="empty">No bills are due this week.</span></sui-data-table>
</div>
A row click emits suiRowClick, a right click suiRowContextMenu with the pointer position, a focused row suiRowFocus. Headers, boxes and filter menus report through suiSort, suiSelectionChange and suiFilter; suiViewWidthChange carries the scroller width. Row actions bubble; state reports do not, so listen on the table.
Steps through the columns button, each sortable header, the filter boxes, the row body as a single stop, and then the footer controls.
ArrowDownArrowUp
Moves the focused row, and scrolls it into view when the rows are virtual.
HomeEnd
Focuses the first or the last row on the page.
PageUpPageDown
Moves the focused row by one viewport.
Space
Selects or clears the focused row, unless the selection mode is none.
Enter
Activates the focused row and emits suiRowClick.
ArrowRightArrowLeft
Opens or closes the focused row when there is a row detail, or opens and closes a branch under row-tree, where the back arrow on a leaf moves to its parent. Under dir="rtl" the pair swaps, so the arrow that points along the reading direction is always the forward one.
F2
Opens the editor on the focused row, or on its first editable cell in cell mode.
Alt+ArrowDown
On a header, opens the column filter menu when filter-display is menu.
EnterSpace on a filter-row mode button
Opens the list of match modes for that column; the arrows move through it and Enter picks one.
Enter in a filter menu
Applies the menu from a text or number field. Tab walks the menu controls; past the last one it closes the menu and carries on from the funnel.
Escape
Cancels an edit, and closes the columns or filter menu without applying it. Inside the menu, an open list or calendar closes first.
Shift+click
On a header, adds a sort term when sort-mode is multiple. On a row, selects the run from the last one.
API
<sui-data-table>
Properties
Property
Attribute
Type
Default
Description
bare
bare
boolean
false
Drops the table's own border and corners, for a table that fills a sui-card (or any frame of the page's) edge to edge: the card is then the one frame, where two borders a pixel apart doubled the edge and the table's rounded corners stopped short of the card's. Leave it off where the table sits inside padded content and needs its own frame. Reflected.
breakpoint
breakpoint
number
0
Frame width in px under which each row becomes a stacked card with its header as a label per cell. Zero leaves the table as a table at every width.
columnLayout
property only
SuiGridLayout
{}
Column order, dragged widths and hidden columns. Mutable; a resize, a reorder and the column menu all rewrite it before suiColumnLayoutChange fires. The same shape sui-data-grid takes. Property, not attribute.
columns
property only
SuiDataTableColumn[]
[]
Column definitions. key is the identity and the field read from the row; width and flex size the column, render draws the cell, pin freezes it against an edge. Property, not attribute.
Whether a double click or F2 opens one cell's editor, every editor in the row, or nothing. A column takes part only if it declares an editor. Enter commits and Escape cancels.
emptyFilterMessage
empty-filter-message
stringstring | undefined
Text for a table whose rows the filter row, the filter menu or globalFilter narrowed to none, and under lazy for no rows while a filter is set. An empty list and a search that found nothing are two states, so this is a second prop; an empty string silences it the same way.
emptyMessage
empty-message
stringstring | undefined
Text for a table with no rows at all. An empty string draws the empty row without words, for a host that says it once above the table. Never apologizes; pair it with the empty-action slot.
expandedRows
property only
SuiKey[]
[]
Keys of the expanded rows. Mutable; the expander button rewrites it before suiExpandedRowsChange fires. Property, not attribute.
filterDisplay
filter-display
"menu" | "none" | "row"
'none'
Where the filter controls live: nowhere, in a row under the headers, or behind a funnel button on each header's end edge. The menu offers up to two conditions suited to the column's type, joined by all or any, or a list of values to tick when the column sets filterOptions; it edits a draft and writes it on Apply.
filterModel
property only
SuiGridFilterModelSuiGridFilterModel | undefined
The filter model: one rule per column plus an optional global term. Named for the model, since filter on the list elements is the boolean that shows a search box; the table's boxes are filterDisplay. Mutable; the filter row and the filter menu rewrite it before suiFilter fires. Property, not attribute.
footerMeta
footer-meta
stringstring | undefined
Faint meta line at the foot of the frame ("4 of 143 rows").
footerTotals
property only
string | undefined
Footer totals, by column key, so a reordered or hidden column keeps its total. Property, not attribute.
globalFilter
global-filter
stringstring | undefined
One term searched across several columns at once. Set it from a search box in the header slot; it is merged into filterModel as the global term.
globalFilterFields
property only
string[]string[] | undefined
Columns the global term searches. Defaults to every visible column; name them to keep an expensive computed column out of the search. Property, not attribute.
gridlines
gridlines
boolean
false
Draws a rule between columns as well as between rows.
groupBy
group-by
stringstring | undefined
Column key to group by. The rows are ordered by it first, ascending unless sorts names it, then by the sort terms within each group, and paged last, so a second sort never splits a group and a page never shows a slice of the grouping. A subheader over each group carries the whole group's count and summary; a group that runs past the end of a page repeats its subheader at the top of the next. Under lazy the host orders and pages the rows, and the table groups what arrives.
Text drawn at the end of a group subheader, given that group's rows and its value. Property, not attribute.
hideSelectionBar
hide-selection-bar
boolean
false
Leaves a selected row its tint alone, without the accent bar the table draws at its start, as sui-list draws it, so that a selection reads without leaning on the tint.
lazy
lazy
boolean
false
The host owns filtering, sorting and paging, and the table renders rows as they arrive. Turn it on for a server-backed table and act on suiSort, suiFilter and suiPageChange.
loading
loading
boolean
false
Renders four shimmer rows in place of data, marks the body aria-busy and names the wait in a status line a reader hears. Reflected, so a page can style the wait.
nullOrder
null-order
SuiGridNullOrder"first" | "last"
'last'
Where blanks land, in both directions. last keeps the empty rows out of the way whichever way the column is sorted.
overscan
overscan
number
6
Rows kept mounted beyond each edge of the viewport while virtual is on. The window leans into the direction of travel, so this is a floor rather than a fixed count.
page
page
number
1
Current page, 1-based. Mutable; a sui-paginator in the footer slot writes it.
pageSize
page-size
number
0
Rows per page. Zero shows every row, which is the default: the frame scrolls instead. A page counts data rows only; the group subheaders over them are lines on top of the count.
Row identity (row, index) => key; defaults to the row index. Selection, expansion and the edit events all carry this key, so a table whose rows reorder needs a real one. Property, not attribute.
rowPartial
property only
ReadonlySet<SuiKey> | undefined
Rows whose checkbox draws as mixed. The table paints this state and never infers it: what makes a row partly selected is the shape of the caller's data, not anything the table holds. Property, not attribute.
Where each row sits in a hierarchy, by row key. Setting it makes the table a treegrid: the first column indents and carries the expander, the rows take aria-level and aria-expanded, and the forward and back arrows walk the levels. Hand it the whole outline in rows, in document order: the table skips the rows under a closed branch itself, so a collapse hides the descendants without the host rewriting rows on suiExpandedRowsChange. Needs a rowKey, since the map is keyed by it. A Map rather than a callback, so a rendered row costs one lookup and no allocation. A flex first column keeps room for the deepest indent and about a dozen characters, so a narrow table scrolls sideways before it cuts a nested name. Property, not attribute.
rows
property only
SuiGridRow[]
[]
Row records, read by column key. Pass a new array to change the data: the filter and sort pipeline is memoized on identity, so a mutated array returns the previous answer. Property, not attribute.
Which rows are selected. include lists the selected keys; exclude means everything except the listed keys, which is how select-all survives a page turn over data the client does not hold. Mutable. Property, not attribute.
selectionMode
selection-mode
"checkbox" | "multiple" | "none" | "single"
'none'
How rows are selected: not at all, one at a time, many by Shift and Ctrl or Cmd, or many with a checkbox column and a select-all box.
showColumnToggle
show-column-toggle
boolean
false
Adds the columns button above the table, which turns columns off and on.
sortIndicator
sort-indicator
"all" | "sorted"
'sorted'
Which sortable headers draw a glyph at rest. sorted draws the arrow on the sorted column only, and the other sortable headers show theirs on hover and on focus; all draws a glyph on every sortable header.
sortMode
sort-mode
"multiple" | "single"
'single'
Whether a header click replaces the sort or adds to it. Under multiple a Shift+click adds a term; under either, a third click on a column drops it.
sorts
property only
SuiGridSort[]
[]
Sort terms, applied in order: the first decides and ties fall to the next. Mutable; a header click rewrites it before suiSort fires. Property, not attribute.
stateKey
state-key
stringstring | undefined
Storage key for the sorts, filters, column layout and page. Leave it unset and the table remembers nothing. On first load a value the host set wins over the saved one and a prop still at its default takes the saved value, so a search or a sort the page set before the table loaded survives; the merged arrangement is written back so the next load agrees with it. Changing the key later loads the whole arrangement saved under the new one.
stateStorage
state-storage
"local" | "session"
'session'
Where stateKey writes: this tab only, or across tabs and restarts.
striped
striped
boolean
false
Tints every second row.
totalRecords
total-records
numbernumber | undefined
Total rows behind a lazy table, for the row count and the paginator.
viewHeight
view-height
stringstring | undefined
Caps the height of the scrolling rows area, as a CSS length such as 360px or 50vh. A bare number is read as pixels. The header stays stuck to its top.
virtual
virtual
boolean
false
Renders only the rows near the viewport. Uniform row height only: every line, including group subheaders and expanded detail panels, is virtualRowHeight tall. Needs viewHeight or a height on the host, because a window with no viewport has nothing to window against.
virtualRowHeight
virtual-row-height
number
0
Row height in px while virtual is on. Zero takes the height the density implies.
Events
Event
Detail
React
Vue
Description
suiCellEdit
SuiGridCellEditDetail
onSuiCellEdit
@sui-cell-edit
Fires once per changed column when an edit commits. Detail is a SuiGridCellEditDetail, the shape sui-data-grid reports too: { data, rowKey, sourceIndex, rowIndex, columnKey, value, previous }. The table does not write the row, so apply the value through rowKey and hand back a new rows array.
suiColumnLayoutChange
SuiGridLayout
onSuiColumnLayoutChange
@sui-column-layout-change
Fires after a column resize, reorder or toggle; detail is the whole columnLayout. Does not bubble.
suiExpandedRowsChange
SuiKey[]
onSuiExpandedRowsChange
@sui-expanded-rows-change
Fires when a row opens or closes; detail is the new expandedRows. Does not bubble.
suiFilter
SuiGridFilterModel
onSuiFilter
@sui-filter
Fires when the filter row, the filter menu or the global term changes the filter; detail is the whole model. Does not bubble.
suiPageChange
number
onSuiPageChange
@sui-page-change
Fires when the page changes, by the paginator or by a page size that moved it; detail is the new page. pageSize is already written, so a lazy host reads both here. Does not bubble.
suiPageSizeChange
number
onSuiPageSizeChange
@sui-page-size-change
Fires when the paginator's rows-per-page control changes the page size; detail is the new pageSize. A page size that moves the page writes both props, then fires this and suiPageChange. Does not bubble.
suiRowClick
SuiDataTableRowEventDetail
onSuiRowClick
@sui-row-click
Fires when a row is clicked or activated with Enter; detail is { row, key, index, x, y }.
suiRowContextMenu
SuiDataTableRowEventDetail
onSuiRowContextMenu
@sui-row-context-menu
Fires on a right click over a row; detail carries the pointer position so a sui-context-menu can open there. The browser's own menu is suppressed.
suiRowFocus
SuiDataTableRowEventDetail
onSuiRowFocus
@sui-row-focus
Fires when a row, or a control inside it such as its checkbox, receives focus; detail is { row, key, index, x, y } with x and y zero. Focus moving between a row and its own controls does not fire it again. A host opens a menu or runs a shortcut for the focused row from this rather than by reading a cell. Does not bubble.
Fires when the selection changes; detail is the new selection. Under mode: 'include' its keys are the selected rows; under mode: 'exclude' every row is selected except its keys, which is how select-all reads over rows the page does not hold. Does not bubble.
suiSort
SuiGridSort[]
onSuiSort
@sui-sort
Fires when a header click changes the sort; detail is the new ordered term list. Does not bubble.
suiViewWidthChange
number
onSuiViewWidthChange
@sui-view-width-change
Fires when the scroller's client width changes, the first measurement after load included; detail is the width in px, what getViewWidth() answers. It comes through the table's own ResizeObserver, so it is at most one per frame, and a host that hides or sizes columns from it writes columnLayout in the frame the width changed, which the table already allows for. Does not bubble.
Methods
Method
Signature
Description
exportCsv()
exportCsv(filename?: string) => Promise<boolean>
Downloads the filtered, sorted rows as CSV, in the column order and visibility on screen. Values run through each column's value or field and then its format, so the file says what the table says. Text a spreadsheet would read as a formula is escaped, because the file is opened by someone who did not watch it being made.
getViewWidth()
getViewWidth() => Promise<number>
The scroller's client width right now, for a caller that needs it outside suiViewWidthChange. Zero before the first measurement.
Applies a whole filter model the way the filter row and the menus do: it becomes filterModel, the table returns to page 1, the model is stored under stateKey, and it goes out on suiFilter, followed by suiPageChange when the table was on another page. What sui-filter-builder calls.
setFocus()
setFocus() => Promise<void>
Focuses the row that holds the table's one tab stop: the row focused last, or the first row in view. With no rows, it focuses the first control in the header. el.focus() on the host does the same.
Slots
Slot
Description
empty
Replaces the empty sentence when there are no rows at all, the slot every list in the family takes; a filter that finds nothing keeps emptyFilterMessage.
empty-action
Link or button under the empty message offering the next step. It stays when the message is an empty string.
footer
Controls under the rows, inside the frame. A sui-paginator here is wired up: the table keeps its totalRecords in step with the filtered count and adopts its page.
header
Controls above the table: a search box, a sui-filter-builder, a filter chip row.
selection-actions
What to do with the selected rows: the count, Change status, Delete, Clear. While any row is selected it takes the header's place in the top bar, Columns button included, and the bar keeps the taller of the two heights, so the rows never move when a selection starts or ends. Give it a role="toolbar" and an aria-label, and say the count in words.
Types
SuiDataTableColumninterfaceA column, plus the three things a table does that a grid does not: filter it from the header, edit it in place, and total it in the footer. Everything else comes from SuiGridColumn.
key
string
Unique identity, and the row field read unless field or value says otherwise.
header
string
Column heading.
headerSr?
string
Heading read by assistive technology when header is empty: an actions column shows no words and still names itself. Ignored when header has text.
field?
string
Row field to read, when it is not key.
value?
(row: SuiGridRow) => unknown
Computed value for sorting, filtering and export. Takes precedence over field; a column with only a render and no value sorts and exports by its raw field, which is usually wrong.
Cell content. A string renders as text; an element is mounted into the cell. Called only for cells inside the rendered window. Property, not attribute.
type?
SuiGridValueType
Drives the default comparator, the default filter match modes and the default alignment.
align?
SuiGridAlign
A side of the screen to align to, in either reading direction. Leave it unset for reading order: text at the inline start, numbers and money at the inline end.
money?
boolean
Aligned to the inline end, tabular numerals, weight 640. Implies type: 'number' for comparison unless the column says otherwise, because a money column holds an amount.
nowrap?
boolean
One line, clipped with an ellipsis, and the full value on the cell's title.
width?
number
Starting width in px. Omit to let flex share the space.
minWidth?
number
maxWidth?
number
flex?
number
Share of the space left after fixed widths. flex: 1 on two columns splits it evenly.
sortable?
boolean
resizable?
boolean
Draggable edge on the header. Off by default; a resized column drops its flex.
hidden?
boolean
Excluded from the render, from export and from the keyboard, but kept in the model so a column toggle can bring it back without the caller rebuilding the array.
pin?
SuiGridPin
Pins the column to an edge. Pinned columns are never windowed away.
compare?
(a: unknown, b: unknown) => number
Custom comparator; overrides type. Return negative, zero or positive.
format?
(value: unknown, row: SuiGridRow) => string
Formats the value for text: the cell when there is no render, the export, and the clipboard. Keep it cheap; it runs once per visible cell per render. Without it a number or money column shows a plain figure grouped and marked in the page's locale (1,234.5, 1.234,5), while the export, the clipboard and an edit keep the plain figure (1234.5).
muted?
boolean
Drawn in --ink-muted: a memo, an account type, anything secondary.
headerTitle?
string
Header tooltip, for a column whose heading had to be short.
editable?
boolean
Opts a column out of in-cell editing on a grid that has it on. A column that draws its own cell is never editable: there is no text for an editor to stand in for.
Extra classes for this column's cells, per row. Called once per visible cell per render, so keep it cheap: return a stable string rather than building one. The grid's own classes are always applied; this adds to them.
filter?
boolean
Offers this column a filter control, in the filter row or the filter menu. Ignored while the element's filterDisplay is none.
matchMode?
SuiGridMatchMode
Match mode the column's filter opens with. Defaults by type: contains for text, equals for numbers and booleans, dateIs for dates.
The values a status-like column holds. With them, the column's filter menu is a checklist of these values, writing an in rule, instead of a list of conditions. A bare string is its own value and label. The filter row ignores them and keeps its text box.
editor?
SuiGridEditor
The control this column opens for editing. A plain text input when it is absent. Named by value rather than by a string, so an application that edits only text never carries the date picker: see the note at the top of editing.ts.
validator?
SuiGridValidator
Checked when an edit commits. Returning a message refuses the commit and keeps the editor open with the message beside it.
SuiDataTableRowEventDetailinterfaceWhat suiRowClick, suiRowContextMenu and suiRowFocus carry. x and y are viewport coordinates and are zero for a click or a focus, which need no position.
row
SuiGridRow
key
SuiKey
index
number
Position in the rows on screen, after filtering, sorting and paging.
x
number
y
number
SuiDataTableRowTreeinterfaceWhere one row sits in a hierarchy. Supplying rowTree is the whole of the table's tree affordance: it draws the indent, the expander, the treegrid roles and the levels, and it computes none of them. Node types, checkbox propagation and lazy children belong to sui-tree-table and to utils/grid/tree.ts.
level
number
Zero at the root. Drives the indent and aria-level, which is one-based, so the table adds one.
expandable
boolean
True for a branch, including one whose children have not arrived. A branch draws the twisty and answers the arrow keys; a leaf keeps the indent and nothing else.
loading?
boolean
The branch is fetching its children: the twisty becomes a spinner and the row is aria-busy.
size?
number
Siblings this row has, including itself, for aria-setsize. Omit both this and pos and the table writes neither, because a reader is better served by silence than by a made-up count.
pos?
number
This row's one-based place among those siblings, for aria-posinset.
SuiGridAligntypeCell text alignment. A side named here is a side of the screen in either reading direction. Left unset, a money or number column sits at the inline end and everything else at the inline start, so the default mirrors under dir="rtl".
No members.
SuiGridCellEditDetailinterfaceOne committed cell edit: the detail of suiCellEdit on sui-data-grid and on sui-data-table. Neither element writes the row. It reports the edit and the host applies it, the same props-in, events-out contract every element here keeps. Each field says what it holds, so no name means a record in one element and a position in the other: the record is data, its identity rowKey, its place in rowssourceIndex, its place on screen rowIndex, and the column columnKey. Write through rowKey or sourceIndex, which survive a sort and a filter; rowIndex names a different record as soon as the view reorders.
data
SuiGridRow
The record, as the element read it.
rowKey
SuiKey
rowKey(data). An element with no rowKey keys a row by its index, which a sort moves, so a host that writes edits back should give it one.
sourceIndex
number
The record's index in rows, or -1 when it is no longer there.
rowIndex
number
The row's position on screen, after filtering, sorting and paging; -1 when a grid's undo or redo lands on a record the filter now hides.
columnKey
string
The edited column's key.
value
string
The new text, as the cell shows text and as the column's editor reads it: a formatted column's editor opens on its formatted text, so parse it back for a number or a date.
previous
string
The text the cell showed before, in the same form.
source?
'undo' | 'redo'
Where the edit came from. The grid replays an undo and a redo as ordinary edits with the values the other way round, and says so here: a host that logs changes, or writes them to a server, usually treats a correction differently from a fresh entry. Absent means a person typed it.
SuiGridCellValuetypeWhat a cell renderer may return. A string is text; an element is appended to the cell. Shared with utils/render-content.ts, which does the mounting.
No members.
SuiGridColumninterfaceOne column. key is both the field read from the row and the column's identity, so it must be unique across the set; field overrides the field when the two need to differ (two columns over the same field, or a computed column with no field at all).
key
string
Unique identity, and the row field read unless field or value says otherwise.
header
string
Column heading.
headerSr?
string
Heading read by assistive technology when header is empty: an actions column shows no words and still names itself. Ignored when header has text.
field?
string
Row field to read, when it is not key.
value?
(row: SuiGridRow) => unknown
Computed value for sorting, filtering and export. Takes precedence over field; a column with only a render and no value sorts and exports by its raw field, which is usually wrong.
Cell content. A string renders as text; an element is mounted into the cell. Called only for cells inside the rendered window. Property, not attribute.
type?
SuiGridValueType
Drives the default comparator, the default filter match modes and the default alignment.
align?
SuiGridAlign
A side of the screen to align to, in either reading direction. Leave it unset for reading order: text at the inline start, numbers and money at the inline end.
money?
boolean
Aligned to the inline end, tabular numerals, weight 640. Implies type: 'number' for comparison unless the column says otherwise, because a money column holds an amount.
nowrap?
boolean
One line, clipped with an ellipsis, and the full value on the cell's title.
width?
number
Starting width in px. Omit to let flex share the space.
minWidth?
number
maxWidth?
number
flex?
number
Share of the space left after fixed widths. flex: 1 on two columns splits it evenly.
sortable?
boolean
resizable?
boolean
Draggable edge on the header. Off by default; a resized column drops its flex.
hidden?
boolean
Excluded from the render, from export and from the keyboard, but kept in the model so a column toggle can bring it back without the caller rebuilding the array.
pin?
SuiGridPin
Pins the column to an edge. Pinned columns are never windowed away.
compare?
(a: unknown, b: unknown) => number
Custom comparator; overrides type. Return negative, zero or positive.
format?
(value: unknown, row: SuiGridRow) => string
Formats the value for text: the cell when there is no render, the export, and the clipboard. Keep it cheap; it runs once per visible cell per render. Without it a number or money column shows a plain figure grouped and marked in the page's locale (1,234.5, 1.234,5), while the export, the clipboard and an edit keep the plain figure (1234.5).
muted?
boolean
Drawn in --ink-muted: a memo, an account type, anything secondary.
headerTitle?
string
Header tooltip, for a column whose heading had to be short.
editable?
boolean
Opts a column out of in-cell editing on a grid that has it on. A column that draws its own cell is never editable: there is no text for an editor to stand in for.
Extra classes for this column's cells, per row. Called once per visible cell per render, so keep it cheap: return a stable string rather than building one. The grid's own classes are always applied; this adds to them.
filter?
boolean
Offers this column a filter control, in the filter row or the filter menu. Ignored while the element's filterDisplay is none.
matchMode?
SuiGridMatchMode
Match mode the column's filter opens with. Defaults by type: contains for text, equals for numbers and booleans, dateIs for dates.
The values a status-like column holds. With them, the column's filter menu is a checklist of these values, writing an in rule, instead of a list of conditions. A bare string is its own value and label. The filter row ignores them and keeps its text box.
editor?
SuiGridEditor
The control this column opens for editing. A plain text input when it is absent. Named by value rather than by a string, so an application that edits only text never carries the date picker: see the note at the top of editing.ts.
validator?
SuiGridValidator
Checked when an edit commits. Returning a message refuses the commit and keeps the editor open with the message beside it.
SuiGridDensitytypeRow height and cell padding. The three stops are the library's control heights, so a grid row lines up with the inputs beside it.
No members.
SuiGridEditorinterfaceA control the grid mounts into a cell, focuses, and reads back when the edit commits. In sui-data-grid focus leaving the control commits, and focus in the list or calendar the control opened has not left it, so a ShineUI picker works as an editor.
mount
(ctx: SuiGridEditorContext) => HTMLElement
Builds the control, populated and ready. The grid mounts it in the cell and focuses it.
read
(el: HTMLElement) => string
The text to commit, read from the element mount returned.
handlesKey?
(e: KeyboardEvent, el: HTMLElement) => boolean
Optional: keys this editor consumes itself, so the grid does not also act on them.
SuiGridEditorContextinterfaceWhat an editor is given when it opens.
value
unknown
The raw value from the row.
text
string
What the cell was showing, which is what a text editor seeds with.
column
SuiGridColumn
row
SuiGridRow
seed?
string
Seed text when the editor was opened by a printable keystroke, else undefined.
SuiGridExcludeSelectioninterfaceEverything is selected except these keys.
mode
'exclude'
keys
ReadonlySet<SuiKey>
SuiGridFilterConditioninterfaceOne condition of an advanced filter: a column, a mode and a value, the same test a constraint makes, with the column it reads named beside it.
key
string
matchMode
SuiGridMatchMode
value?
unknown
SuiGridFilterConstraintinterfaceOne test. value carries whatever the mode needs: a scalar, an array for in and notIn, a two-element [from, to] for between, and nothing at all for empty and notEmpty.
value?
unknown
matchMode
SuiGridMatchMode
SuiGridFilterGroupinterfaceConditions and groups joined by one operator, to any depth: "Owner is Davon, and any of (health is Critical, no next meeting, no interaction)". What sui-filter-builder edits, and the one shape in the model that can join two columns with or.
operator
'and' | 'or'
items
(SuiGridFilterCondition | SuiGridFilterGroup)[]
SuiGridFilterModelinterfaceThe whole filter state. Rules are keyed by column so a header can ask "is this column filtered" in one lookup; the global term is a sibling rather than a magic key inside the map, because it is a different shape and a map that holds two shapes cannot be typed. tree holds what only a tree can say, conditions across columns joined by or or nested; the three are joined by and.
rules?
Readonly<Record<string, SuiGridFilterRule>>
global?
SuiGridGlobalFilter
tree?
SuiGridFilterGroup
SuiGridFilterRuleinterfaceEvery constraint on one column, and how they combine. and is the default a filter menu opens with; or is what a user reaches for when one column can hold two acceptable answers.
key
string
The column this rule filters. It is repeated inside the rule, rather than being left to the map key in SuiGridFilterModel, because a rule also travels alone in an event detail and in saved state, where there is no map key to read. The evaluator reads this field, so the two can never quietly disagree about which column a rule applies to.
operator
'and' | 'or'
constraints
SuiGridFilterConstraint[]
SuiGridGlobalFilterinterfaceOne term searched across several columns at once. keys is required rather than defaulting to every column, for the same reason PrimeVue makes globalFilterFields explicit: a computed column's value function would otherwise be called once per row per keystroke without anyone having asked for it.
value
string
keys
readonly string[]
matchMode?
SuiGridMatchMode
Defaults to contains.
SuiGridIncludeSelectioninterfaceThe keys that are selected.
mode
'include'
keys
ReadonlySet<SuiKey>
SuiGridLayoutinterfaceThe arrangement a user has made of the columns, and the shape both elements persist. It lives here rather than on either element because a saved layout has to survive being handed from one to the other: a team that outgrows the table and moves a screen to the grid should not have to migrate the column widths their users dragged. Keys the order does not name keep their declared position behind the ones it does, so adding a column in code never invalidates a layout saved before that column existed.
order?
string[]
Column keys in display order.
widths?
Record<string, number>
Dragged widths in px, by column key. A dragged column is fixed and drops its flex.
hidden?
string[]
Column keys a column toggle has turned off.
SuiGridMatchModetypeHow one constraint tests a value. Declared here rather than in filter.ts because a column names one and every element reads it, which is what this file is for; the evaluator that implements them is in filter.ts, which re-exports this so both can be imported together. The first sixteen are PrimeVue's FilterMatchMode verbatim. Three are additions, and they are additions rather than oversights: notIn because in without its negation is an odd half of a pair, and empty / notEmpty because "rows with no memo" is the filter a ledger reaches for most and PrimeVue cannot express it at all (an empty filter value means "no filter" there, so emptiness is exactly the one thing a value-carrying constraint can never ask about).
No members.
SuiGridNullOrdertypeWhere nulls and blanks land, independent of direction: last keeps empties out of the way in both directions, which is what a ledger wants.
No members.
SuiGridPintypeWhich edge a column is pinned to, if any. The grid renders a pane per edge; the table uses position: sticky cells, because a native table cannot be split into panes.
No members.
SuiGridRowtypeA row is the host's own record. The elements read fields by column key, print them and compare them; they never assume a shape beyond that.
No members.
SuiGridRowSelectiontypeWhich rows are selected. exclude is not a convenience. It is the only honest way to express select-all over a dataset whose total the client does not hold: a server-backed grid matching four million rows cannot enumerate them, and an allSelected boolean beside a key set falls apart the moment the user unticks three of them. "Everything except these three" survives that, survives paging, and is what a bulk action posts to a server as a predicate rather than as a list.
No members.
SuiGridSortinterfaceOne sort term. The elements sort by an ordered list of these, so single-column sort is a list of one and no second code path exists for it.
key
string
dir
SuiGridSortDir
SuiGridSortDirtype
No members.
SuiGridValidatortypeReturns null when the value is acceptable, or the message to show when it is not.
No members.
SuiGridValueTypetypeHow a column's values are compared, formatted and filtered when the column does not say otherwise. text is the fallback for anything unrecognised.
No members.
SuiKeytypeA record's or an item's key wherever the library identifies one: a row, a tree node, a list item, a selected value. Numbers stay numbers, so a numeric id round-trips through selection, the events and a framework binding without turning into a string.
No members.
Theming
Every design token this element can render with, read from its own stylesheets and the ones it imports. Set a token on :root or on any ancestor and every use below follows it. The values shown are the ones in the theme you are viewing. The list is read per stylesheet, so an element that imports a shared drawing module lists that module's tokens too, whether or not it paints with all of them.