DataGrid
Since 0.1.0-alpha.20Static server-side rendering: no circuit, no WebAssembly.
A data grid with editing, grouping, filters and columns people arrange, its state in the address so it works on static pages too. Cells take arrow keys, a row can open a detail, columns hide to fit a phone, and it exports to CSV and Excel.
Usage
<DataGrid Items="orders" ItemKey="o => o.Id" PageSize="20" Caption="Orders">
<PropertyColumn Property="o => o.Id" Title="Order" Width="8.Rem" Frozen="ColumnFrozen.Start" />
<PropertyColumn Property="o => o.Customer" Sortable Filterable MinWidth="12.Rem" />
<PropertyColumn Property="o => o.Total" Format="C" Sortable Width="10.Rem" />
</DataGrid>Topics
- Columns: Let people resize, move, hide and freeze columns.
- Filtering: A filter on each column, a row of filters, one of your own, and a builder for what they can't hold.
- Aggregates: Totals over every row the filters match, in the footer.
- Grouping: Group by columns, a page of groups at a time, with values for each group.
- Master-detail: Open a row's detail under it, such as its lines in a grid of their own.
- Adaptability: Fit phones and narrow spaces: hide columns that don't fit, and show their values under the row.
- Editing: Edit in a dialog, in the row, or a cell at a time.
- Export: Download what's on screen as Excel or CSV.
Columns
Give a column a Width to keep it, and the others share the rest down to their MinWidth, when the grid scrolls sideways. Shift+click a header to add it to the sort.
Preview
Code
@* Order, Date and Total keep their widths; Customer and Status share the rest, and past their
minimums the grid scrolls sideways. Shift+click a header to add it to the sort. *@
Rows = IzzyBlazor.Site.Examples.Table.Orders
<DataGrid Items="Rows.All" ItemKey="o => o.Number.ToString()" PageSize="5" PageSizeOptions="[5, 10]" InitialSort="-date" Caption="Orders" SelectionMode="SelectionMode.Multiple" Grow>
<PropertyColumn Property="o => o.Number" Title="Order" Align="CellAlign.Start" Width="6.Rem" />
<PropertyColumn Property="o => o.Customer" Sortable Filterable MinWidth="12.Rem" />
<PropertyColumn Property="o => o.Date" Format="MMM d" Sortable Width="8.Rem" />
<TemplateColumn Title="Status" SortBy="o => o.Status" SortKey="status" Sortable>
<Badge Status="Rows.StatusOf(context.Status)" Dot>context.Status</Badge>
</TemplateColumn>
<PropertyColumn Property="o => o.Total" Format="C" Sortable Width="8.Rem" />
</DataGrid>Row appearance
Striped alternates the rows' backgrounds, ColumnLines adds a line between columns, ShadedHeader shades the header, and RowLines set to false takes away the line between rows. RowHover highlights the row under the pointer, to help follow a wide one across. It's off by default, as a highlight suggests a row does something: a row with a RowHref always has it.
Preview
| Order | Customer | Date | Total |
|---|---|---|---|
| 1001 | Margaret Hamilton | 9/8/2026 | $66.25 |
| 1002 | Katherine Johnson | 9/15/2026 | $108.00 |
| 1003 | Grace Hopper | 9/22/2026 | $149.75 |
| 1004 | Barbara Liskov | 9/29/2026 | $191.50 |
| 1005 | Linus Torvalds | 10/6/2026 | $233.25 |
Code
Rows = IzzyBlazor.Site.Examples.Table.Orders
@* Stripes, a line between columns, and a highlight under the pointer. Lines between rows are on unless
RowLines is false. *@
<DataGrid Items="Rows.All" PageSize="5" Caption="Orders with stripes and lines" Striped ColumnLines RowHover Grow>
<PropertyColumn Property="o => o.Number" Title="Order" Align="CellAlign.Start" Width="6.Rem" />
<PropertyColumn Property="o => o.Customer" />
<PropertyColumn Property="o => o.Date" Format="d" />
<PropertyColumn Property="o => o.Total" Format="C" Width="9.Rem" />
</DataGrid>Toolbar and search
ToolbarContent puts your own ToolbarButtons above the grid, and Searchable adds a search box across the text columns, with the words in the address. While rows are selected, the bar of SelectionActions takes the toolbar's place, and SelectAllMode says what the header checkbox takes: the page, or every row the filters match.
Preview
Code
@* Your own buttons above the grid, and a search box whose words are in the address. Select a row and
the bar of SelectionActions takes the toolbar's place, so the rows stay where they are. *@
Rows = IzzyBlazor.Site.Examples.Table.Orders
<DataGrid Items="Rows.All" ItemKey="o => o.Number.ToString()" PageSize="5" Caption="Orders with tools" SelectionMode="SelectionMode.Multiple" SelectAllMode="SelectAllMode.All" Searchable QueryPrefix="tools-" SelectionName="tools" Grow>
<ToolbarContent>
<ToolbarButton StartIcon="Lucide.Plus" Position="ToolbarPosition.End" Variant="ButtonVariant.Outline">New order</ToolbarButton>
<ToolbarButton StartIcon="Lucide.Download" Position="ToolbarPosition.End">Export</ToolbarButton>
</ToolbarContent>
<SelectionActions>
<Button Size="ControlSize.Sm" Variant="ButtonVariant.Outline" Accent="Accent.Neutral" StartIcon="Lucide.Archive">Archive</Button>
</SelectionActions>
<ChildContent>
<PropertyColumn Property="o => o.Number" Title="Order" Align="CellAlign.Start" Width="6.Rem" />
<PropertyColumn Property="o => o.Customer" Sortable />
<PropertyColumn Property="o => o.Date" Format="MMM d" Sortable Width="8.Rem" />
<PropertyColumn Property="o => o.Total" Format="C" Sortable Width="8.Rem" />
</ChildContent>
</DataGrid>Row menus
An ActionColumn with Overflow set to RowActionOverflow.Always gives each row a ⋮ menu of RowActions, and with script a right-click, a long press, Shift+F10 or the menu key open the same menu. It acts on the row it opened on, whatever is selected. A right-click on a link keeps the browser's menu.
Controls in cells
Tab moves into the grid once, and arrow keys move between its cells. A cell with one button or link focuses it directly, so Enter uses it. In a cell with several controls or a field, press Enter to go in and Escape to come back.
Preview
| Order | Customer | Note | Priority | Actions | More |
|---|---|---|---|---|---|
| 1001 | Margaret Hamilton | ||||
| 1002 | Katherine Johnson | ||||
| 1003 | Grace Hopper | ||||
| 1004 | Barbara Liskov | ||||
| 1005 | Linus Torvalds | ||||
| 1006 | Alan Turing | ||||
| 1007 | Ada Lovelace | ||||
| 1008 | Margaret Hamilton | ||||
| 1009 | Katherine Johnson | ||||
| 1010 | Grace Hopper | ||||
| 1011 | Barbara Liskov | ||||
| 1012 | Linus Torvalds | ||||
| 1013 | Alan Turing | ||||
| 1014 | Ada Lovelace | ||||
| 1015 | Margaret Hamilton | ||||
| 1016 | Katherine Johnson | ||||
| 1017 | Grace Hopper | ||||
| 1018 | Barbara Liskov | ||||
| 1019 | Linus Torvalds | ||||
| 1020 | Alan Turing | ||||
| 1021 | Ada Lovelace | ||||
| 1022 | Margaret Hamilton | ||||
| 1023 | Katherine Johnson |
Code
@* Cells that hold controls, small to keep the rows' height. One button or menu takes focus directly;
with several, or a field that uses the arrows itself, Enter goes in and Escape comes back. The
header stays in view when the rows scroll. *@
Rows = IzzyBlazor.Site.Examples.Table.Orders
<DataGrid Items="Rows.All" ItemKey="o => o.Number.ToString()" MaxHeight="16.Rem" Caption="Orders to review" Grow>
<PropertyColumn Property="o => o.Number" Title="Order" Align="CellAlign.Start" Width="6.Rem" />
<PropertyColumn Property="o => o.Customer" Sortable Width="12.Rem" />
<TemplateColumn Title="Note" Context="order">
{ var review = _reviews[order.Number]; }
<TextField @bind-Value="review.Note" Label="($"Note for order {order.Number}")" HideLabel Size="ControlSize.Sm" />
</TemplateColumn>
<TemplateColumn Title="Priority" Width="8.Rem" Context="order">
{ var review = _reviews[order.Number]; }
<Select @bind-Value="review.Priority" Items="Priorities" Label="($"Priority of order {order.Number}")" HideLabel Size="ControlSize.Sm" />
</TemplateColumn>
<TemplateColumn Title="Actions" Width="10.Rem" Context="order">
<Row Gap="1">
<Button Size="ControlSize.Sm" Variant="ButtonVariant.Outline" Accent="Accent.Neutral">Open</Button>
<Button Size="ControlSize.Sm" Variant="ButtonVariant.Ghost" Accent="Accent.Neutral">Copy</Button>
</Row>
</TemplateColumn>
<TemplateColumn Title="More" Width="4.Rem" Context="order">
<Menu>
<Trigger Context="wire">
<IconButton Icon="Lucide.Ellipsis" Label="($"Actions for order {order.Number}")" Variant="ButtonVariant.Ghost" Accent="Accent.Neutral" Size="ControlSize.Sm" @attributes="wire" />
</Trigger>
<ChildContent>
<MenuItem>Archive</MenuItem>
</ChildContent>
</Menu>
</TemplateColumn>
</DataGrid>
{
private static readonly string?[] Priorities = ["Normal", "High", "Low"];
// Each order's own note and priority, not one shared by every row.
private readonly Dictionary<int, Review> _reviews = Rows.All.ToDictionary(o => o.Number, o => new Review { Priority = o.Number % 4 == 1 ? "High" : "Normal" });
private sealed class Review
{
public string? Note { get; set; }
public string? Priority { get; set; }
}
}Virtual rows
Virtualize draws only the rows in view, on an interactive page, so 100,000 rows scroll as easily as 100. Give it a MaxHeight, and keep rows to one line of RowHeight. On a static page, as here unless you switch this page to Interactive, the same grid loads more rows on scroll instead.
Preview
| Select | Number | Customer | Total |
|---|---|---|---|
| 1 | Katherine Johnson | $108.00 | |
| 2 | Grace Hopper | $149.75 | |
| 3 | Barbara Liskov | $191.50 | |
| 4 | Linus Torvalds | $233.25 | |
| 5 | Alan Turing | $275.00 | |
| 6 | Ada Lovelace | $316.75 | |
| 7 | Margaret Hamilton | $358.50 | |
| 8 | Katherine Johnson | $400.25 | |
| 9 | Grace Hopper | $442.00 | |
| 10 | Barbara Liskov | $483.75 | |
| 11 | Linus Torvalds | $65.50 | |
| 12 | Alan Turing | $107.25 | |
| 13 | Ada Lovelace | $149.00 | |
| 14 | Margaret Hamilton | $190.75 | |
| 15 | Katherine Johnson | $232.50 | |
| 16 | Grace Hopper | $274.25 | |
| 17 | Barbara Liskov | $316.00 | |
| 18 | Linus Torvalds | $357.75 | |
| 19 | Alan Turing | $399.50 | |
| 20 | Ada Lovelace | $441.25 |
Code
@* 100,000 rows, of which only those in view are drawn, each window read as a database would be. Switch
this page to Interactive to see it; a static page loads more on scroll instead. *@
IzzyBlazor.Site.Examples.Table
<DataGrid TItem="Order" ItemsProvider="LoadAsync" ItemKey="o => o.Number.ToString()" Caption="Every order" Virtualize RowHeight="40" MaxHeight="24.Rem" PageSize="20" SelectionMode="SelectionMode.Multiple" SelectionName="every" QueryPrefix="every-" Grow>
<PropertyColumn Property="o => o.Number" Sortable Align="CellAlign.Start" Width="8.Rem" />
<PropertyColumn Property="o => o.Customer" />
<PropertyColumn Property="o => o.Total" Format="C" Width="8.Rem" />
</DataGrid>
{
private static readonly IReadOnlyList<Order> Many = Enumerable.Range(1, 100_000)
.Select(i => Orders.All[i % Orders.All.Count] with { Number = i })
.ToList();
private async ValueTask<DataResult<Order>> LoadAsync(DataRequest<Order> request)
{
// As long as a query takes, so you can see rows on their way.
if (RendererInfo.IsInteractive)
{
await Task.Delay(150, request.CancellationToken);
}
return request.ToResult(Many.AsQueryable());
}
}Placing the toolbar's tools
Write DataGridSearch, DataGridExport or DataGridColumnPicker in ToolbarContent to place the grid's own tools among yours, in the order written; one you don't write keeps its usual place, and the grid's parameter still turns it on. Write them directly in ToolbarContent, not inside a component of your own. Export and Columns show only their icons with a hover tooltip, as suits a grid used all day: ToolbarLabels adds text to both, and ShowText to one.
Preview
Code
@* The grid's own tools among yours, in the order written. Export has its text; the others are as usual. *@
Rows = IzzyBlazor.Site.Examples.Table.Orders
<DataGrid Items="Rows.All" PageSize="5" Caption="Orders with placed tools" Searchable ColumnPicker LayoutKey="order-tools" QueryPrefix="placed-" ExportCsvHref="/examples/orders.csv" ExportExcelHref="/examples/orders.xlsx" Grow>
<ToolbarContent>
<ToolbarButton StartIcon="Lucide.Plus" Variant="ButtonVariant.Outline" Priority="1">Add order</ToolbarButton>
<DataGridSearch />
<DataGridColumnPicker Position="ToolbarPosition.End" />
<ToolbarIconButton Icon="Lucide.CircleQuestionMark" Label="Help" Position="ToolbarPosition.End" />
<DataGridExport Position="ToolbarPosition.End" ShowText />
</ToolbarContent>
<ChildContent>
<PropertyColumn Property="o => o.Number" Align="CellAlign.Start" Width="8.Rem" />
<PropertyColumn Property="o => o.Customer" Sortable />
<PropertyColumn Property="o => o.Total" Format="C" Sortable Width="8.Rem" />
</ChildContent>
</DataGrid>Details
Groupable lets people group by columns from a header's menu, and InitialGroup or Group starts grouped. The grouping and what's open are in the address (?group=status&open=Shipped), so a group's row is a link and this works with no script. A page counts top-level groups, a group is never split, and whatever Paging or Virtualize say, a grouped grid draws numbered pages. Aggregate on a column works out a value over every row the filters match, for the footer and each group's row. With an ItemsProvider, either one needs a GroupsProvider: rows still come from your provider, with the group's values in the filter.
LayoutKey remembers how one person has arranged the columns (widths, order, hidden, frozen) in a cookie, so the server draws it on the first paint with no flash. It's per person and per page; a shared link still shows the same rows, since sort, filter and page are in the address. To keep layouts in a database instead, bind Layout.
ResizableColumns, ReorderableColumns and ColumnPicker let people rearrange the columns, with script, by pointer, keys, a menu in each header and a picker above the grid. On a static page they need somewhere to keep the result: a LayoutKey, or Layout with a LayoutSaveUrl.
A toolbar above the grid holds ToolbarContent (Toolbar's items), the search box with Searchable, export and the Columns button, which show their icons alone unless ToolbarLabels is set. Write DataGridSearch, DataGridExport or DataGridColumnPicker in ToolbarContent to place one among your items. While rows are selected, the bar of SelectionActions takes its place. Searchable searches the text columns; the words go in the address (?search=), and with script apply as you type.
Virtualize draws only the rows in view, for tens of thousands of them, on an interactive page: give it a MaxHeight, and keep rows to one line of RowHeight. An ItemsProvider gets each window as a Skip and Take, and returns the Total. On a static page it loads on scroll instead, by PageSize (50 if not set), up to 2,000 rows. If the focused row scrolls away, focus waits on the scroll box and goes back when it returns, or on the next key, which scrolls to it. Browser find, and Shift ranges of selection, reach the rows drawn, unless SelectAllMode is All.
ExportExcelHref, and ExportCsvHref beside it if CSV is wanted too, add plain links to endpoints the app maps with DataExport. With both, one button with a chevron opens a menu of them. They carry the filter, sort, search and visible columns, in every mode and with no script. The endpoint's fields decide what's written, and head the file. A download matches the screen when they have the grid's keys: a column whose key they don't have is left out, a filter on it matches nothing, and a sort on it is ignored. Search covers their searchable fields, which may not be the grid's text columns. Text doesn't carry FilterCase, so a grid that sets it needs the same Case on those fields.
ActionColumn holds a row's actions in a last column: EditAction, DeleteAction and your own RowActions, each a button, or with Overflow="RowActionOverflow.Always" in a ⋮ menu, which works with no script. In a grid that hides columns, buttons move into the menu once one is hidden, unless they say Never. A grid with editing has one by itself. With script a right-click on the row, a long press, Shift+F10 or the menu key opens the row's menu. It acts on the row it opened on, and doesn't change the selection; actions on the selection go in the bar. A right-click on a link keeps the browser's menu, with its "open in new tab": that includes the whole of a row with RowHref, whose ⋮ button and Shift+F10 still open the menu.
EditMode with OnSave edits a row, with no script needed. Each column gets a field chosen by its type (a text area for text with [DataType(DataType.MultilineText)]) if it can be edited, shown or not: set Editable="false" on what can't be, such as the key, which a save can't change. Visible="false" starts a column hidden, for a field the table doesn't show, or for people to show from the picker. Each row has an Edit link, which puts its key in the address (?edit=42), and a save is a form post. Only editable columns take a value from it, so a forged field changes nothing. OnSave gets a copy of the row, and either finishes, which closes the editor and loads the rows again, or adds errors, which stay on the fields. A grid with editing can't be inside another form, as forms can't nest. It needs an ItemKey, and on a static page, a row type Blazor can fill from a form: public settable properties.
<DataGrid Items="orders" ItemKey="o => o.Id.ToString()" EditMode="DataGridEditMode.Dialog" OnSave="SaveAsync">
<PropertyColumn Property="o => o.Id" Editable="false" />
<PropertyColumn Property="o => o.Customer" />
<PropertyColumn Property="o => o.Notes" Visible="false" />
</DataGrid>DataGridEditMode.Row edits where the row stands, with Save and Cancel in place of its actions, also with no script. Only the columns shown have a field, so a hidden one keeps its value. A new row starts at the top of the page. Sorting, filtering or changing page ends the edit, and what's typed goes with it. It can't be used with Virtualize or loading on scroll, or with an EditFormTemplate.
DataGridEditMode.Cell edits one cell at a time on an interactive page, and OnSave runs for each one that changed. A click, Enter, F2 or typing starts it; Tab, Enter or a click elsewhere saves it, and an invalid cell stays open. A new row is edited as with Row, and so is every row on a static page. Besides the limits of Row, it can't be used with RowHref.
Parts
Written inside DataGrid. The API page lists their parameters.
ActionColumn<TItem>: the column that holds a row's actions, at the end of a DataGrid.RowAction: an action of your own on a row: an icon button with a tooltip, or an item in its menu.EditAction: the row's edit action: a pencil, or an item in its menu.DeleteAction: the row's delete action, which asks first: a trash can, or an item in its menu.EditLayout<TItem>: sets up a DataGrid's generated edit dialog.EditField: a column's generated field, placed in an EditLayout<T>.EditFormTemplate<TItem>: your own form in a DataGrid's edit dialog, in place of the generated one, for what isn't fields of the row, such as steps or a save across several records.DataGridAdd: puts a DataGrid's Add button, from NewItem, among your toolbar items.DataGridSearch: puts a DataGrid's search box, from Searchable, among your toolbar items.DataGridExport: puts a DataGrid's export, from ExportExcelHref and ExportCsvHref, among your toolbar items: a link with one of them, or with both a button with a chevron that opens a menu of links.DataGridFilterBuilder: puts a DataGrid's Filter builder button, from FilterBuilder, among your toolbar items.DataGridColumnPicker: puts a DataGrid's Columns button, from ColumnPicker, among your toolbar items.
Mode support
| Feature | Static | Interactive |
|---|---|---|
| Sort, filter and page through the URL | Yes | Yes |
| Column widths and sideways scrolling, set by the server | Yes | Yes |
| Frozen columns | Yes | Yes |
| Groups from all the data, a page of them, each opened by a link | Yes | Yes |
| Aggregates over everything the filters match, in the footer and on groups | Yes | Yes |
| Virtual rows on interactive pages, loading on scroll on static ones | Yes | Yes |
| A toolbar with search in the address, its tools placed among yours | Yes | Yes |
| CSV and Excel export, streamed from an endpoint you map | Yes | Yes |
| Editing a row in a dialog, saved by a form post | Yes | Yes |
| Row menus (right-click, long press and keys open them too) | Yes | Yes |
| Resizing, moving and hiding columns, kept per person | Yes | Yes |
| Selected rows post with a form | Yes | Yes |
| Shift+click sorts by several columns | Yes | Yes |
| One Tab stop and arrow keys between cells, with selection keys | Yes | Yes |
Static: static server-side rendering, with no circuit and no WebAssembly. Interactive: Server, WebAssembly or Auto.
Without JavaScript
Every page starts as plain HTML: before IzzyBlazor's script loads on a static page, while an interactive page prerenders, and with JavaScript off. With JavaScript off, there's no select-all checkbox and Shift+click doesn't add to the sort; filters apply when you press Apply; with columns hidden to fit, a row being edited shows their values in its detail, not fields.
Accessibility
Captionnames the grid for screen readers; it isn't shown.- With script it's an ARIA grid, one Tab stop, with the keys below. A cell with one link, button or checkbox focuses that directly. With no script, it's a plain table and Tab goes through its controls.
- Resizing and moving a column need no dragging: a header's keys and menu do both, and a new width is read out.
- Sortable headers are links, and the first sorted column has
aria-sort. With several sorted, each says its place and direction ("Sort 2, descending"). - Row checkboxes and filter buttons are named as in a Table.
- The toolbar is one Tab stop with script, with arrow keys between its controls. The tools that show only an icon are named for what they do, and have that as a hover tooltip.
Keyboard navigation
In the rows
| Press this key | To do this |
|---|---|
| Tab | Move into the grid, and on past it |
| Up or Down | Move to the row above or below |
| Left or Right | Move to the cell before or after |
| Home or End | Move to the first or last cell of the row |
| Ctrl+Home or Ctrl+End | Move to the first cell of the first row, or the last cell of the last |
| PageUp or PageDown | Move by the rows in view; from the edge of a page, to the next page |
| Enter or F2 | Go into a cell with several controls or a field |
| F2 | Edit the row, from a cell of text, in a grid that edits rows |
| Enter | On a row that's a link, follow it, from a cell of text |
| Alt+Down | Open the cell's menu |
| Shift+F10 or Menu | Open the row's menu (on a link, only Shift+F10) |
Inside a cell
| Press this key | To do this |
|---|---|
| Tab | Move among its controls, and back out past either end |
| Up or Down or Left or Right | On a button or link, move on to another cell; a field keeps them |
| Escape or F2 | Go back to the cell |
Groups and details
| Press this key | To do this |
|---|---|
| Right or Left | On a group's row, open or close it |
| Enter | On a group's row, or a row's detail button, open or close it |
| Down | From a row, move to its open detail, one stop whatever it holds |
| Enter or F2 | Go into the detail |
| Escape | Go back to the detail from inside it, a grid in it too |
| Tab | Inside the detail, move among its controls; from a grid in it, on past this grid |
In a row being edited
| Press this key | To do this |
|---|---|
| Tab or Shift+Tab | Move through its fields, then Save and Cancel |
| Enter | In a field, save |
| Escape | Cancel, once any open list or picker has closed |
With DataGridEditMode.Cell, on an interactive page
| Press this key | To do this |
|---|---|
| Enter or F2 or typing | Edit the cell, from a cell that can be edited |
| Tab or Shift+Tab | Save it, and edit the next or the one before |
| Enter | Save it, and go back to the cell |
| Escape | Cancel, and go back to the cell |
Selecting rows
| Press this key | To do this |
|---|---|
| Space | Select or clear the row, from a cell of text |
| Shift+Space | Select or clear the row, from anywhere in it but a field |
| Shift+Up or Shift+Down | Extend the selection, or shrink it going back |
| Ctrl+A | Select what's on the page, or the rows drawn when they're virtual; every match with SelectAllMode.All |
On a header
| Press this key | To do this |
|---|---|
| Enter | Sort by the column, if it sorts |
| Shift+Enter | Add the column to the sort; again, reverse it, then take it out |
| Alt+Down | Open its menu, or its filter when there's no menu |
| F2 | Go in to its controls, the sort link first |
| Shift+F10 or Menu | Open its menu |
| Shift+Left or Shift+Right | Resize the column, where it can be |
| Alt+Left or Alt+Right | Move the column, where it can be |
| PageDown | Go to the first row |
In the footer
| Press this key | To do this |
|---|---|
| Tab | Move through the page size button and the pager's links |
| Enter | Follow the link |
In the filter row
| Press this key | To do this |
|---|---|
| Up or Down | Move to the header above or the row below |
| Left or Right | Move to the filter before or after |
| Enter or F2 | Go into the filter's field |
| Alt+Down | Open the column's filter panel |
Inside a filter cell
| Press this key | To do this |
|---|---|
| Tab or Shift+Tab | Move between the field and the filter button, and back out past either end |
| Enter | In the field, filter now without waiting |
| Alt+Down | In the field, open the column's filter panel |
| Escape or F2 | Go back to the cell; the filter stays |
In a filter panel
| Press this key | To do this |
|---|---|
| Tab or Shift+Tab | Move among the operator, the values and the list |
| Enter | Filter now and close, from a field or the list's search box |
| Escape | Close and go back to the filter button; the filter stays |
In a filter's list
| Press this key | To do this |
|---|---|
| Down | Move from the search box to the first value |
| Up or Down | Move among the values; Up from the first goes to the search box |
| Home or End | Move to the first or last value |
| Space | Tick or untick the value |
Parameters
| Prop | Type | Default |
|---|---|---|
DetailTemplateA row's detail: drawn under it, across the grid, while it's open. | RenderFragment<TItem>? | |
ItemHasDetailWhich rows have a detail; all of them when it's not set. | Func<TItem, bool>? | |
@bind-OpenDetailsWhich rows' details are open, by key; "*" is all of them, and "!key" an exception to that. | IReadOnlyList<string>? | |
DetailsOpenWhether every row's detail starts open. | bool | false |
SingleDetailOpening a row's detail closes the one that was open. | bool | false |
GroupableLets people group by columns, from a header's menu. | bool | false |
@bind-GroupThe grouping, outermost first, such as "status,-customer" (- for descending); null for InitialGroup. | string? | |
InitialGroupThe grouping when the URL has none, such as "status". | string? | |
GroupsOpenWhether groups start open. | bool | false |
@bind-OpenGroupsWhich groups are open, each by its values joined with /, escaped, such as "Shipped/Acme"; "*" is all of them, and "!Shipped" an exception to that. | IReadOnlyList<string>? | |
GroupRowLimitThe most rows drawn in one open group. | int | 100 |
GroupPanelKeeps the bar of what's grouped above the grid while nothing is, saying a header can be dragged there. | bool | false |
ShowGroupedColumnsKeeps a grouped column in the table. | bool | false |
SmallScreenWhat it does when its columns don't fit its width: DataGridSmallScreen.Scroll (the default) scrolls sideways; DataGridSmallScreen.HideColumns hides columns by their Priority and shows their values in the row's detail. | DataGridSmallScreen | DataGridSmallScreen.Scroll |
NonceA CSP nonce for the style element that holds the widths where columns hide. | string? | computed |
EditModeHow a row is edited. | DataGridEditMode | DataGridEditMode.None |
FindItemFinds a row that isn't among those loaded, by its key, for the edit dialog and the delete confirmation: a link to a row on another page, say. | Func<string, Task<TItem>>? | |
NewItemA new row's starting values. | Func<TItem>? | |
ItemEditableWhich rows can be edited; all of them when it's not set. | Func<TItem, bool>? | |
ItemDeletableWhich rows can be deleted; all of them when it's not set. | Func<TItem, bool>? | |
@bind-EditKeyThe key of the row being edited, or null. | string? | |
EditCopyMakes the copy of a row that's edited, on an interactive page, in place of a shallow copy: for a type that holds what a copy mustn't share. | Func<TItem, TItem>? | |
LayoutKeyRemembers how each person has arranged the columns, in a cookie under this name, so the server draws it on the first paint. | string? | |
@bind-LayoutHow the columns are arranged, in place of the cookie: bind it (@bind-Layout) to keep layouts yourself, such as per user in a database. | DataGridLayout? | |
LayoutSaveUrlWith script, an address the app maps to save a person's layout: the grid posts it there as JSON when it changes, for a static page that passes its own Layout. | string? | |
ResizableColumnsLets people resize columns: by dragging a handle at the header's end, by double-clicking it to fit, by Shift+Left and Shift+Right, or from the header's menu. | bool | false |
ReorderableColumnsLets people move columns: by dragging a header, by Alt+Left and Alt+Right, or from the header's menu, which can also freeze them. | bool | false |
ColumnPickerA button above the grid listing the columns to show or hide, and to put back as declared. | bool | false |
VirtualizeDraws only the rows in view, on an interactive page; on a static page they load on scroll instead, by PageSize (50 if not set), up to 2,000 rows. | bool | false |
RowHeightThe height of every row, in pixels, with Virtualize. | double | 48 |
ToolbarLabelsText beside the icons of the toolbar's own tools, for a grid people use now and then. | bool | false |
ExportCsvHrefA link in the toolbar, in a menu with ExportExcelHref, to an endpoint that writes the rows as CSV with DataExport, for a grid that offers CSV besides Excel. | string? | |
ExportExcelHrefA link in the toolbar, in a menu with ExportCsvHref, to an endpoint that writes the rows as an Excel workbook with DataExport. | string? | |
ToolbarContentThe page's own controls in the toolbar above the grid: ToolbarButton, ToolbarMenu and the like. | RenderFragment? | |
SearchableA search box in the toolbar, across the text columns. | bool | false |
@bind-SearchThe words searched for. | string? | |
SearchLabelThe search box's name and placeholder. | string? | "Search" |
FilterRowAdds a row of filters under the titles: a field for each column, then its filter button, which opens the column's panel, where the operator is chosen. | bool | false |
FilterBuilderAdds a Filter builder button to the toolbar, for groups of conditions no column's filter can hold. | bool | false |
GroupsProviderCounts and aggregates for an ItemsProvider, which a column's Aggregate needs. | Func<DataGroupRequest<TItem>, ValueTask<DataGroupResult>>? | |
ChildContentThe columns: PropertyColumn<T1, T2> and TemplateColumn<T>. | RenderFragment? | |
InitialSortThe sort when the URL has none, such as "name" or "-date" (descending). | string? | |
@bind-SortThe sort, such as "name" or "-date" (descending), or several in a DataGrid ("status,-date"); null for InitialSort. | string? | |
@bind-FilterThe filter: Filter.All of a condition per filtered column (contains for text, between for numbers, equals for dates, equals or in for choices), or null for none. | Filter? | |
FilterCaseHow text compares in the column filters and search. | FilterCase | FilterCase.IgnoreCase |
FilterPanelA row below the rows that lists the active filters, each with a button to remove it, and Clear for all of them. | bool | false |
CaptionThe table's name for screen readers (a visually hidden caption). | string? | |
RowClassA class for each row, such as one that highlights overdue orders. | Func<TItem, string?>? | |
RowHoverHighlights the row under the pointer, to help follow a wide one across. | bool | false |
StripedAlternating row backgrounds. | bool | false |
RowLinesA line between rows. | bool | true |
ColumnLinesA line between columns. | bool | false |
ShadedHeaderShades the header, and a DataGrid's FilterRow with it, so they read apart from the rows; its color is --ib-table-head-bg. | bool | false |
BorderedA rounded border around the table. | bool | true |
SizeRow height and text size. | ControlSize | ControlSize.Md |
MaxHeightA height limit: the rows scroll and the header stays in view. | Length? | |
RowHrefMakes each row a link, such as to the item's details page. | Func<TItem, string?>? | |
RowCurrentThe row for what the page is showing, such as the one whose details are in a panel beside the table: drawn as selected, and marked aria-current (on its link with RowHref). | Func<TItem, bool>? | |
RowLabelNames a row for screen readers, in its link and its checkbox. | Func<TItem, string>? | computed |
FilterFromAddressApplies a whole filter from the address (?filter=, with the QueryPrefix), as a FilterBuilder<T> on the page writes it, along with the columns' own. | bool | false |
ItemsThe items. | IEnumerable<TItem>? | |
ItemsProviderLoads the items yourself, in place of Items: the request has the range (and in a Table the sort, the filter and the columns as fields), so request.ToResultAsync(query, ...) or request.Apply(query) does the work. | Func<DataRequest<TItem>, ValueTask<DataResult<TItem>>>? | |
@bind-PageSizeItems per page. | int? | |
PageSizeOptionsSizes to choose from in a "Rows per page" menu in the footer, such as new[] { 10, 25, 50 }. | IReadOnlyList<int>? | |
@bind-PageThe page, from 1. | int? | |
PagingHow it moves through its pages when it has a PageSize: numbered pages (DataPaging.Pages, the default, which needs a total), or by cursor, with no total needed: First, Previous and Next (DataPaging.NextPrevious), a "Load more" button after the items (DataPaging.LoadMore, ListView only), or loading as the last item comes into view (DataPaging.LoadOnScroll). | DataPaging | DataPaging.Pages |
LoadMoreTextThe "Load more" button's text, such as "Load earlier". | string? | "Load more" |
@bind-CursorThe cursor of the page shown when paging by cursor (Paging); null for the first page. | string? | |
QueryPrefixPrefix for its query-string keys, so several can share a page: QueryPrefix="orders-" uses ?orders-page= (and a Table's ?orders-sort=). | string | "" |
EmptyContentShown when there are no items. | RenderFragment? | computed |
EmptyTextThe text when there are no items. | string? | "No items" |
ErrorContentShown in place of "Couldn't load the rows." when loading fails, before the Retry button. | RenderFragment? | |
ItemKeyA unique, stable key for each item, such as its id. | Func<TItem, string>? | computed |
LoadingShows it as loading: items dim, or placeholders show while there are none. | bool | false |
SelectionModeWhether items can be selected: one (a radio button on each) or several (a checkbox on each). | SelectionMode | SelectionMode.None |
SelectionNameThe form field name the selection posts under, read back as a Selection with [SupplyParameterFromForm(Name = ...)]. | string? | computed |
@bind-SelectionThe selected items: keys on any page, or every item matching the filter except some. | Selection? | |
ItemSelectableWhether an item can be selected; others show a disabled, unchecked box, and selecting the page or a range leaves them out. | Func<TItem, bool>? | |
SelectAllModeWhat the select-all checkbox selects: the items on this page, or every item the filters match, on any page. | SelectAllMode | SelectAllMode.Page |
SelectionActionsActions on the selection, in a bar above the items, disabled until something is selected (on a DataGrid with a toolbar, the bar takes the toolbar's place while something is). | RenderFragment<Selection>? | |
SelectAllLabelThe select-all checkbox's name for screen readers. | string? | "Select all rows" |
SelectRowLabelThe start of each item checkbox's name for screen readers, before the item's label. | string? | "Select" |
OnSaveSaves an edited row. | EventCallback<DataGridSave<TItem>> | |
OnDeleteDeletes a row, once someone has confirmed it. | EventCallback<DataGridDelete<TItem>> |
Plus 16 item props and 4 common props.
Full APISee also
- Table: sort, filter, page and select, with state in the URL.