DataGrid
A data table that sorts, filters, pages and selects, locally or on your server, with columns the reader can resize, reorder, pin and hide.
Import
import { DataGrid } from '@vitral/vue';Sort, filter, page and select
Click a header to sort, type under it to filter (accents are ignored: “sao” finds São Paulo), search every column at once from the header, and tick rows.
| Ximena Diallo | São Paulo · Brazil | Sep 15, 2020 | €4,586.25 | Offline | |
| Noémie Nowak | Zürich · Switzerland | Oct 7, 2023 | €5,484.63 | Offline | |
| Bruno Nowak | Kraków · Poland | Dec 23, 2022 | €456.73 | Away | |
| Quentin Gómez | Tōkyō · Japan | Nov 26, 2023 | €5,444.92 | Active | |
| Hanna Mendes | Reykjavík · Iceland | Jun 9, 2021 | €16,745.23 | Away | |
| Wiktor Lima | Kraków · Poland | Jul 15, 2020 | €6,428.86 | Active | |
| Bruno Rocha | Tōkyō · Japan | Aug 26, 2021 | €8,297.10 | Active | |
| Gustavo Rocha | Québec · Canada | Apr 15, 2021 | €10,280.87 | Active |
Multiple sort, single selection, grid lines
Sorted by country, then by balance, highest first. Ctrl-click (⌘-click) a header to add it to the sort. Click a row, or focus the rows and use the arrows and Space, to choose one. The header stays in view while the body scrolls.
| Diego Ferreira | Argentina | Córdoba | €15,492.29 |
| Noémie Fontaine | Argentina | Córdoba | €9,472.17 |
| Ximena Ferreira | Argentina | Córdoba | €2,865.18 |
| Bruno Martin | Brazil | Florianópolis | €17,876.33 |
| Élise Dubois | Brazil | São Paulo | €14,269.79 |
| Bruno Souza | Brazil | Florianópolis | €13,363.03 |
| Bruno Lima | Brazil | São Paulo | €12,562.40 |
| Ana Gómez | Brazil | Recife | €11,750.51 |
| Fatou Ferreira | Brazil | São Paulo | €10,543.41 |
| Oskar Martin | Brazil | São Paulo | €10,400.34 |
| Ximena Diallo | Brazil | São Paulo | €4,586.25 |
| Bruno Rocha | Canada | Montréal | €17,103.69 |
| João Etxeberria | Canada | Montréal | €11,302.35 |
| Wiktor Jónsson | Canada | Québec | €10,857.06 |
| Gustavo Rocha | Canada | Québec | €10,280.87 |
| Élise Sato | Canada | Québec | €4,759.90 |
| Hanna Souza | Canada | Québec | €2,684.86 |
| Ximena Souza | Colombia | Bogotá | €9,305.62 |
| Valéria Fontaine | Colombia | Bogotá | €7,948.90 |
| Ana Jónsson | Colombia | Bogotá | €4,625.47 |
| Ana Lima | Germany | München | €15,860.46 |
| Kenji Costa | Germany | Köln | €11,732.89 |
| Hanna Martin | Germany | München | €7,969.50 |
| Mateus Dubois | Germany | Köln | €3,976.73 |
| João Etxeberria | Germany | Köln | €3,947.82 |
| Carla Lima | Germany | München | €2,002.67 |
| Hanna Mendes | Iceland | Reykjavík | €16,745.23 |
| Diego Costa | Iceland | Reykjavík | €13,914.59 |
| Fatou Fontaine | Iceland | Reykjavík | €13,839.42 |
| Wiktor Fontaine | Iceland | Reykjavík | €13,239.60 |
| Tomás Dubois | Iceland | Reykjavík | €12,242.07 |
| Tomás Ferreira | Iceland | Reykjavík | €8,764.27 |
| Bruno Rocha | Japan | Tōkyō | €8,297.10 |
| Kenji Costa | Japan | Tōkyō | €8,098.99 |
| Carla Diallo | Japan | Tōkyō | €5,945.52 |
| Quentin Gómez | Japan | Tōkyō | €5,444.92 |
| João Martin | Japan | Tōkyō | €5,028.76 |
| Valéria Álvarez | Japan | Tōkyō | €2,964.27 |
| Wiktor Costa | Japan | Tōkyō | -€1,790.27 |
| Fatou Rocha | Japan | Tōkyō | -€1,940.60 |
| Iñaki Dubois | Poland | Kraków | €17,709.64 |
| Wiktor Costa | Poland | Kraków | €17,144.37 |
| João Gómez | Poland | Kraków | €12,230.50 |
| Wiktor Lima | Poland | Kraków | €6,428.86 |
| Wiktor Ferreira | Poland | Kraków | €1,503.44 |
| Wiktor Costa | Poland | Kraków | €504.85 |
| Bruno Nowak | Poland | Kraków | €456.73 |
| Wiktor Souza | Poland | Kraków | -€303.08 |
| Fatou Ferreira | Portugal | Évora | €12,014.06 |
| Fatou Jónsson | Portugal | Lisboa | €10,703.89 |
| Ana Etxeberria | Portugal | Lisboa | €7,618.88 |
| Raquel Diallo | Portugal | Lisboa | -€493.76 |
| Bruno Lima | Spain | Málaga | €17,131.64 |
| Noémie Sato | Spain | Málaga | €9,951.09 |
| João Nowak | Spain | Málaga | €4,612.00 |
| Fatou Gómez | Spain | Málaga | €755.90 |
| Kenji Nowak | Spain | Málaga | -€776.43 |
| Carla Fontaine | Switzerland | Zürich | €15,325.86 |
| Ximena Etxeberria | Switzerland | Zürich | €11,252.24 |
| Søren Müller | Switzerland | Zürich | €11,236.83 |
| Élise Lima | Switzerland | Zürich | €8,895.92 |
| Noémie Nowak | Switzerland | Zürich | €5,484.63 |
| Zoë Mendes | Switzerland | Zürich | €2,202.54 |
| Wiktor Lima | Switzerland | Zürich | €1,071.43 |
Columns the reader arranges
Drag a header to move a column, or hold Ctrl (⌘) and press an arrow. Drag the edge of a header to set its width — the handle is in the tab order, so the arrow keys do it too, and Shift makes bigger steps. Name and Balance are pinned to the edges and stay while the rest scrolls sideways. The button opens the list of columns. Everything the reader does is one object, `columnLayout`, which an application can store and hand back.
| Role | Status | Balance | |||
|---|---|---|---|---|---|
| Ximena Diallo | Brazil | São Paulo | Offline | €4,586.25 | |
| Noémie Nowak | Switzerland | Zürich | Offline | €5,484.63 | |
| Bruno Nowak | Poland | Kraków | Away | €456.73 | |
| Quentin Gómez | Japan | Tōkyō | Active | €5,444.92 | |
| Hanna Mendes | Iceland | Reykjavík | Away | €16,745.23 | |
| Wiktor Lima | Poland | Kraków | Active | €6,428.86 | |
| Bruno Rocha | Japan | Tōkyō | Active | €8,297.10 | |
| Gustavo Rocha | Canada | Québec | Active | €10,280.87 |
Two tables, one set of columns
`group` is a name two tables share: rearrange a column in either and both follow, and scrolling one sideways scrolls the other. The rows are each table's own — here, this quarter above last quarter.
| Name | Country | City | Balance |
|---|---|---|---|
| Ximena Diallo | Brazil | São Paulo | €4,586.25 |
| Noémie Nowak | Switzerland | Zürich | €5,484.63 |
| Bruno Nowak | Poland | Kraków | €456.73 |
| Quentin Gómez | Japan | Tōkyō | €5,444.92 |
| Name | Country | City | Balance |
|---|---|---|---|
| Hanna Mendes | Iceland | Reykjavík | €16,745.23 |
| Wiktor Lima | Poland | Kraków | €6,428.86 |
| Bruno Rocha | Japan | Tōkyō | €8,297.10 |
| Gustavo Rocha | Canada | Québec | €10,280.87 |
Grouped, the way a base is read
`group-by` gathers the rows by a field and puts a heading over each run, with its count and a toggle. It happens after the query, so sorting and filtering still decide which rows there are — and the rows are gathered by value rather than by adjacency, so sorting by another column keeps each group together and sorts inside it. `v-model:collapsedGroups` is what the reader shut, which an application can store.
| Ana Etxeberria | Lisboa | Portugal | Active | €7,618.88 |
| Fatou Ferreira | Évora | Portugal | Offline | €12,014.06 |
| Fatou Jónsson | Lisboa | Portugal | Offline | €10,703.89 |
| Raquel Diallo | Lisboa | Portugal | Offline | -€493.76 |
| Ana Gómez | Recife | Brazil | Active | €11,750.51 |
| Bruno Lima | São Paulo | Brazil | Away | €12,562.40 |
| Bruno Martin | Florianópolis | Brazil | Active | €17,876.33 |
| Bruno Souza | Florianópolis | Brazil | Active | €13,363.03 |
| Élise Dubois | São Paulo | Brazil | Active | €14,269.79 |
| Fatou Ferreira | São Paulo | Brazil | Blocked | €10,543.41 |
| Oskar Martin | São Paulo | Brazil | Away | €10,400.34 |
| Ximena Diallo | São Paulo | Brazil | Offline | €4,586.25 |
| Ana Jónsson | Bogotá | Colombia | Offline | €4,625.47 |
| Valéria Fontaine | Bogotá | Colombia | Away | €7,948.90 |
| Ximena Souza | Bogotá | Colombia | Offline | €9,305.62 |
| Ana Lima | München | Germany | Active | €15,860.46 |
| Carla Lima | München | Germany | Away | €2,002.67 |
| Hanna Martin | München | Germany | Blocked | €7,969.50 |
| João Etxeberria | Köln | Germany | Active | €3,947.82 |
| Kenji Costa | Köln | Germany | Active | €11,732.89 |
| Mateus Dubois | Köln | Germany | Away | €3,976.73 |
| Bruno Lima | Málaga | Spain | Active | €17,131.64 |
| Fatou Gómez | Málaga | Spain | Blocked | €755.90 |
| João Nowak | Málaga | Spain | Active | €4,612.00 |
| Kenji Nowak | Málaga | Spain | Active | -€776.43 |
| Noémie Sato | Málaga | Spain | Active | €9,951.09 |
| Bruno Nowak | Kraków | Poland | Away | €456.73 |
| Iñaki Dubois | Kraków | Poland | Blocked | €17,709.64 |
| João Gómez | Kraków | Poland | Away | €12,230.50 |
| Wiktor Costa | Kraków | Poland | Blocked | €17,144.37 |
| Wiktor Costa | Kraków | Poland | Blocked | €504.85 |
| Wiktor Ferreira | Kraków | Poland | Active | €1,503.44 |
| Wiktor Lima | Kraków | Poland | Active | €6,428.86 |
| Wiktor Souza | Kraków | Poland | Active | -€303.08 |
| Bruno Rocha | Tōkyō | Japan | Active | €8,297.10 |
| Carla Diallo | Tōkyō | Japan | Active | €5,945.52 |
| Fatou Rocha | Tōkyō | Japan | Blocked | -€1,940.60 |
| João Martin | Tōkyō | Japan | Away | €5,028.76 |
| Kenji Costa | Tōkyō | Japan | Offline | €8,098.99 |
| Quentin Gómez | Tōkyō | Japan | Active | €5,444.92 |
| Valéria Álvarez | Tōkyō | Japan | Offline | €2,964.27 |
| Wiktor Costa | Tōkyō | Japan | Active | -€1,790.27 |
| Bruno Rocha | Montréal | Canada | Offline | €17,103.69 |
| Élise Sato | Québec | Canada | Offline | €4,759.90 |
| Gustavo Rocha | Québec | Canada | Active | €10,280.87 |
| Hanna Souza | Québec | Canada | Blocked | €2,684.86 |
| João Etxeberria | Montréal | Canada | Away | €11,302.35 |
| Wiktor Jónsson | Québec | Canada | Active | €10,857.06 |
| Carla Fontaine | Zürich | Switzerland | Offline | €15,325.86 |
| Élise Lima | Zürich | Switzerland | Active | €8,895.92 |
| Noémie Nowak | Zürich | Switzerland | Offline | €5,484.63 |
| Søren Müller | Zürich | Switzerland | Away | €11,236.83 |
| Wiktor Lima | Zürich | Switzerland | Active | €1,071.43 |
| Ximena Etxeberria | Zürich | Switzerland | Away | €11,252.24 |
| Zoë Mendes | Zürich | Switzerland | Away | €2,202.54 |
| Diego Costa | Reykjavík | Iceland | Offline | €13,914.59 |
| Fatou Fontaine | Reykjavík | Iceland | Away | €13,839.42 |
| Hanna Mendes | Reykjavík | Iceland | Away | €16,745.23 |
| Tomás Dubois | Reykjavík | Iceland | Active | €12,242.07 |
| Tomás Ferreira | Reykjavík | Iceland | Offline | €8,764.27 |
| Wiktor Fontaine | Reykjavík | Iceland | Away | €13,239.60 |
| Diego Ferreira | Córdoba | Argentina | Away | €15,492.29 |
| Noémie Fontaine | Córdoba | Argentina | Away | €9,472.17 |
| Ximena Ferreira | Córdoba | Argentina | Active | €2,865.18 |
From a data source
The rows come from `createDataSource({ load })`, a stand-in for a server that answers in 450 ms. The table asks it for one page at a time with the sort and the search; a slow answer that arrives after a newer one is thrown away.
| No available options | ||
Empty and loading
| Invoice | Amount |
|---|---|
| No invoices this month | |
| Name | City |
|---|---|
| Ximena Diallo | São Paulo |
| Noémie Nowak | Zürich |
| Bruno Nowak | Kraków |
| Quentin Gómez | Tōkyō |
API
Read from packages/vue/src/components/DataGrid/types.ts, so it says what the component actually accepts.
Props
| Name | Type | Description |
|---|---|---|
| groupBy | string | Gathers the rows by this field and puts a heading over each run, with a count and a toggle. Grouping happens after the query, so sorting and filtering still decide which rows there are; `v-model:collapsedGroups` holds which of them the reader has shut. |
| groupLabel | (context: { value: unknown; count: number; rows: any[] }) => string | What a group's heading says; the value itself by default. |
| value | any[] | — |
| dataKey | string | A field that identifies a row, for selection and row keys. |
| dataSource | DataSourceLike | Rows come from a data source, local or remote, instead of `value`. |
| lazy | boolean | Nothing is computed here: sort, filter and page changes are emitted (`lazy-load`) for the app to answer. |
| totalRecords | number | The total across pages when `lazy`. |
| loading | boolean | — |
| paginator | boolean | — |
| rowsPerPageOptions | number[] | — |
| pageLinkSize | number | — |
| paginatorTemplate | string | ('FirstPageLink' | 'PrevPageLink' | 'PageLinks' | 'NextPageLink' | 'LastPageLink' | 'RowsPerPageDropdown' | 'CurrentPageReport')[] | — |
| currentPageReportTemplate | string | — |
| paginatorPosition | 'top' | 'bottom' | — |
| alwaysShowPaginator | boolean | — |
| sortMode | 'single' | 'multiple' | — |
| removableSort | boolean | A third click on a sorted column takes it out of the sort. |
| globalFilterFields | string[] | — |
| filterDisplay | 'row' | `'row'` puts a filter box under each filterable column's header. |
| filterDelay | number | Milliseconds to wait after typing before a lazy table or a data source is asked again. |
| selectionMode | 'single' | 'multiple' | — |
| resizableColumns | boolean | A handle on every column's trailing edge, dragged (or keyed) to set its width. |
| columnResizeMode | 'fit' | 'expand' | `'fit'` takes the width from the next column, keeping the table's own; `'expand'` widens the table. |
| reorderableColumns | boolean | Columns can be dragged by their header into another order, or moved with Ctrl and an arrow key. |
| columnToggle | boolean | A button that opens the list of columns, each with a checkbox. |
| group | string | A name two tables share: they scroll sideways together and keep one column layout. |
| stripedRows | boolean | — |
| showGridlines | boolean | — |
| size | Size | — |
| scrollable | boolean | — |
| scrollHeight | string | A CSS height, or `'flex'` to fill a flex parent. The header stays in view. |
| emptyMessage | string | — |
| rowClass | (data: any) => unknown | — |
| tableStyle | string | Record<string, string> | — |
| caption | string | Names the table. Hidden unless `showCaption`; without it, name the table with `aria-label`. |
| showCaption | boolean | — |
Plus pt, dt and unstyled from BaseProps, see pass-through and unstyled mode.
Emits
| Event | Payload | Description |
|---|---|---|
| page | event: DataGridPageEvent | — |
| column-resize | event: DataGridColumnEvent & { width: number } | A column was widened or narrowed. |
| column-reorder | event: DataGridColumnEvent & { order: string[] } | A column was moved. |
| column-toggle | event: DataGridColumnEvent & { visible: boolean } | A column was shown or hidden. |
| column-pin | event: DataGridColumnEvent & { side: 'left' | 'right' | null } | A column was stuck to an edge, or let go. |
| sort | event: DataGridSortEvent | — |
| filter | event: DataGridFilterEvent | — |
| lazy-load | event: LoadOptionsLike | — |
| row-click | event: DataGridRowClickEvent | — |
| row-select | event: DataGridRowSelectEvent | — |
| row-unselect | event: DataGridRowSelectEvent | — |
| row-select-all | event: DataGridSelectAllEvent | — |
| row-unselect-all | event: DataGridSelectAllEvent | — |
Slots
| Name | Slot props | Description |
|---|---|---|
| default | — | The `<Column>`s. |
| header | — | — |
| footer | — | — |
| empty | — | — |
| loadingicon | — | — |
| paginatorstart | (props: DataGridPageEvent) | — |
| paginatorend | (props: DataGridPageEvent) | — |