Component

Vue Data Grid with Filtering

<DomDataGrid>

A dense spreadsheet-like grid for database records, with typed column filters, sorting, row selection, and a server-query friendly API. We talk about databases, rendering pipelines, virtual DOMs, and scalable app architecture. But really, they just want Excel.

Quick start

Use DomDataGrid now

Choose the installation path that suits this stage

Use npm for the fastest working version. Copy the source when you want DomDataGrid to become part of your codebase: yours to edit, adapt, and maintain.

View install options

When to use it

Use DomDataGrid for tables of records people scan, sort, filter, select and edit: admin lists, database tables, ledgers, logs and spreadsheet-style screens. It handles virtual scrolling, typed filters, sorting, row selection, inline editing, range copy and paste, grouping with totals, pinned columns and CSV or Excel export.

The grid never changes your data. It renders the rows you pass, reports what the person did through events, and leaves saving to you.

Quick start

vue
Install
npm install @getdom/studio
vue
<script setup>
import '@getdom/studio/style.css';
import { ref } from 'vue';
import { DomDataGrid } from '@getdom/studio';

const rows = ref([
	{ id: 1, customer: 'Atlas Works', status: 'paid', amount: 1234.5, orderedAt: '2026-03-14' },
	{ id: 2, customer: 'Northstar Labs', status: 'pending', amount: 89, orderedAt: '2026-03-15' },
]);

const columns = [
	{ key: 'id', label: 'Order #', type: 'number', width: 96 },
	{ key: 'customer', label: 'Customer' },
	{ key: 'status', label: 'Status', type: 'select', options: ['pending', 'paid', 'shipped'] },
	{ key: 'amount', label: 'Amount', type: 'currency', currency: 'USD', locale: 'en-US' },
	{ key: 'orderedAt', label: 'Ordered', type: 'date' },
];
</script>

<template>
	<DomDataGrid :rows="rows" :columns="columns" title="Orders" resource-label="orders" />
</template>

The grid uses DOM Studio's styles and one custom element for its popovers. Set both up once per app, as the package README describes: import @getdom/studio/style.css (or @getdom/studio/tailwind.css from your own Tailwind stylesheet), and tell Vue that tags starting with dom- are custom elements in vite.config.js:

js
js
vue({ template: { compilerOptions: { isCustomElement: (tag) => tag.startsWith('dom-') } } })

Defaults to check first

The grid starts as a full spreadsheet. Turn off what a simple list doesn't need.

Prop Default Change it when
selectable true Rows can't be selected: :selectable="false".
showRowNumbers true You don't want the 1, 2, 3 column: :show-row-numbers="false".
showColumnLetters true You don't want the A, B, C row above the headers: :show-column-letters="false".
resizableColumns true Columns should keep fixed widths.
toolbar true You render your own title, search and counts: :toolbar="false".
title 'Data grid' Always set it, or hide the toolbar.
height '34rem' This is a maximum height for the scrolling body. Use a CSS size or pixels.
density 'compact' Rows should breathe: density="comfortable".
column currency 'GBP' Set currency on every currency column, such as 'USD'.
column locale 'en-GB' Set locale, such as 'en-US', for US dates and number formats.

Currency cells drop the minor units at 1,000 and above (£1,235). Pass format and formatAggregate when you need exact amounts such as $1,234.50.

Columns

Each column is a plain object. key is required and is also the row property to read unless you set field or accessor. The column type decides sorting, formatting, alignment, the filter control and the editor.

type Sorts as Filter control Editor Default width and alignment
text Case-insensitive text Contains text Text input 13rem, left
number Number Min and max Number input 7rem, right
currency Number Min and max Number input 8.5rem, right
date Date Date range Date input 9.5rem, left
boolean false before true Yes or no Toggle on Space or double-click 7rem, center
select Option value as text Checklist of options Select list 9rem, left

Useful column options:

  • options on select columns: strings or { label, value, tone } objects. tone (green, amber, red, blue) colours the status pill. Always supply options on large data sets; otherwise the grid scans every row to find them.
  • format(value, row, column) returns the display text. Global search matches this text, not the raw value.
  • accessor(row, column) computes a value, such as quantity * price. Columns with an accessor are read-only.
  • sortAccessor(row, column) sorts by something other than the cell value.
  • sortable: false, filterable: false, searchable: false, resizable: false, exportable: false and hidden: true turn features off per column.
  • pinned: 'left' or 'right' keeps the column in view while the grid scrolls sideways.
  • component renders every cell with your Vue component. It receives row, column, value, formatted, rowIndex and rowKey, can emit update:value to change the cell and action to send a command, which the grid re-emits as cell-action.

Store dates as YYYY-MM-DD strings or Date objects. Strings in that form sort, filter and format correctly and are the cheapest to sort.

Sorting, search and filters

Clicking a sortable header cycles ascending, descending, then unsorted. With multi-sort, each header click adds or changes a rule in sort.rules, which a server can translate into a multi-column ORDER BY.

Sorting, search and filters are controlled props, so you can set them from your own controls and keep them in the URL or a store:

vue
Install
npm install @getdom/studio
vue
<script setup>
import '@getdom/studio/style.css';
</script>

<DomDataGrid
	v-model:sort="sort"
	v-model:search="search"
	v-model:filters="filters"
	:rows="rows"
	:columns="columns"
/>
  • sort is { key, direction } with direction 'asc', 'desc' or '', or { rules: [...] }.
  • search matches the formatted text of every searchable column.
  • filters is keyed by column key. Select columns take { type: 'select', values: ['paid', 'shipped'] }, number and currency columns { min, max }, date columns { start, end }, boolean columns { value: true }, and text columns { value: 'atlas', operator: 'contains' } with operator 'contains', 'starts' or 'equals'.

Filters applied from your own controls also show as chips in the toolbar and on the column header, and "Clear filters" clears them.

Editing

Editing takes two steps: let the cells be edited, then write each change back to your rows.

vue
Install
npm install @getdom/studio
vue
<script setup>
import '@getdom/studio/style.css';
function applyChange({ row, column, value }) {
	row[column.field || column.key] = value;
}
</script>

<template>
	<DomDataGrid :rows="rows" :columns="columns" editable cell-selection @cell-update="applyChange" />
</template>
  • editable lets every plain column be edited. Set editable: false on a column to lock it, or editable: true to allow just that column. A function editable(row, column) decides per cell.
  • People double-click a cell to edit it. Add cell-selection for keyboard editing: the grid body becomes one Tab stop, the first arrow key picks a cell, arrow keys move between cells, Enter, F2 or typing starts an edit, Enter saves and moves down, Tab saves and moves right, leaving the cell saves, and Escape cancels.
  • Select columns edit with a native select that includes an empty "-" choice. Add validate if the value is required.
  • cell-selection also adds range selection, copy, cut, paste from Excel or Google Sheets, Delete to clear, and undo or redo.
  • validate(value, row, column) returns true or an error message, which is shown and keeps the old value. Add a check for empty text if a column must not be blank.
  • parse(text, column) converts typed or pasted text before validation.
  • cell-update fires once per changed cell with { row, column, rowIndex, rowKey, value, previousValue, source }. cells-update fires once per edit, paste, delete, undo or redo with every change, so you can save a batch in one request.

Edits change your rows, and sorting, grouping, filters and totals follow, because the grid reads from them.

Selection

Rows have checkboxes by default. Bind v-model:selected-keys to read or set the selection. Keys come from row-key, which defaults to the id field; pass a field name or a function when your rows use another key.

The header checkbox selects or clears every row that matches the current search and filters, including rows that are scrolled out of view.

Grouping and totals

vue
Install
npm install @getdom/studio
vue
<script setup>
import '@getdom/studio/style.css';
</script>

<DomDataGrid v-model:group-by="groupBy" :rows="rows" :columns="columns" show-totals column-menu />
  • group-by takes a column key, or an array of keys for nested groups. Each group row shows its value, a row count and the aggregate of every column that sets aggregate.
  • aggregate is 'sum', 'avg', 'count', 'min', 'max' or a function (values, rows, column).
  • show-totals adds a totals row pinned to the bottom with the same aggregates over all filtered rows.
  • groups-expanded sets whether groups start open. The template ref methods expandAllGroups() and collapseAllGroups() open or close all of them.
  • column-menu adds a menu to every header so people can group by or pin any column themselves.

Export

exportable adds an Export menu to the toolbar with CSV and Excel (.xlsx) downloads. Exports contain the filtered, sorted rows with typed numbers, dates and currency. To export from your own button, call exportCsv() or exportExcel() from the toolbar-actions slot props or a template ref:

vue
Install
npm install @getdom/studio
vue
<script setup>
import '@getdom/studio/style.css';
</script>

<DomDataGrid ref="grid" :rows="rows" :columns="columns" export-file-name="orders" />
<button type="button" @click="grid.exportCsv()">Export CSV</button>

Use exportable: false to leave a column out, and exportValue(value, row, column) to change what is exported and copied.

Large data sets

Add virtual-rows for in-memory data sets above a few hundred rows. Only the rows in view, plus virtual-row-buffer rows above and below, are mounted, and search, filters, sorting, grouping and selection still cover every row. Rows have a fixed height from the density; set virtual-row-height if your custom cells are taller.

For speed on large sets, supply options on select columns and keep dates as YYYY-MM-DD strings.

Server data

Set remote when a server does the searching, filtering, sorting and paging. The grid then renders rows exactly as given and fires query-change with { search, filters, sort } whenever the person changes them.

vue
Install
npm install @getdom/studio
vue
<script setup>
import '@getdom/studio/style.css';
</script>

<DomDataGrid
	:rows="page.rows"
	:columns="columns"
	:total-rows="page.total"
	:loading="loading"
	remote
	@query-change="load"
	@viewport-end="loadMore"
/>

total-rows sets the count in the toolbar. viewport-end fires near the bottom of the scroll area for cursor-based loading.

A resource API can take the query as it comes, with paging beside it. The same endpoint can serve numbered pages and cursor-based scrolling:

js
js
// Request
{
	resource: 'customers',
	query: {
		search: 'atlas',
		filters: {
			status: { type: 'select', values: ['active', 'trial'] },
			arr: { type: 'currency', min: 50000, max: 250000 },
		},
		sort: { key: 'updatedAt', direction: 'desc', rules: [{ key: 'updatedAt', direction: 'desc' }, { key: 'arr', direction: 'desc' }] },
	},
	pagination: { strategy: 'page', page: 2, pageSize: 25 }, // or { strategy: 'cursor', cursor: 'customer:25', limit: 25 }
}

// Response
{
	data: [/* rows */],
	pageInfo: { strategy: 'page', page: 2, pageSize: 25, pageCount: 8, total: 184, hasNextPage: true, nextCursor: 'customer:50' },
}

Toolbar and footer

With toolbar on, the grid shows the title, a search box, filter chips with "Clear filters", the toolbar-actions slot, the Export menu when exportable is set, and a count such as "1,243 of 10,000 orders" using resource-label. The footer shows the column count, "N selected" when rows are selected, and short notices such as "Exported 1243 rows to CSV". Set :toolbar="false" to supply your own.

Layout

  • Drag a header divider to resize a column; right-pinned columns resize from their left edge. Resizing starts from the displayed width and keeps the other column widths steady. Spare space remains empty instead of stretching the columns, and min-column-width limits how far a column can shrink.
  • layout="flush" removes the outer border, rounding and shadow for edge-to-edge pages; gutter-class aligns the outer cell padding with your page gutter.
  • Narrow screens scroll the table sideways inside its frame, and pinned columns unpin themselves when they would leave too little room to scroll.
  • Use the cell slot for one-off cell rendering, toolbar-actions for buttons next to search, and empty for a custom empty state.

Examples

Trading desk: edit, group, pin and export

2,500 positions grouped by desk, with Ticker pinned left and P&L pinned right. Click a cell and use the arrow keys, drag a range to see its sum, double-click or type to edit, paste from a spreadsheet, and export the current view to Excel.

Trading desk positions

2,500 of 2,500 positions

Grouped by Desk
ABCDEFGHIJKLM
#

Ticker

Pin column

Filter Ticker

Instrument

Pin column

Filter Instrument

Desk

Pin column

Filter Desk

Values

Region

Pin column

Filter Region

Values

Quantity

Pin column

Filter Quantity

Price

Pin column

Filter Price

Market value

Pin column

Filter Market value

Day %

Pin column

Filter Day %

Status

Pin column

Filter Status

Values

Trader

Pin column

Filter Trader

Trade date

Pin column

Filter Trade date

Settled

Pin column

Filter Settled

P&L

Pin column

Filter P&L

2,531,604$255.38$652,166,059+0.12%28 Sept 2026500$1,006,273
1CO1004Gold spotCommoditiesAmericas3,670$31.14$114,284+0.23%OpenT. Grant08 Aug 2026Yes$263.00
2CO1009Nat gas JanCommoditiesAmericas8,276$267.96$2,217,637-1.54%OpenM. Okafor25 Sept 2026Yes-$34,152
3CO1014Nat gas JanCommoditiesEMEA3,023$88.26$266,810+3.65%ReviewL. Chen04 Aug 2026Yes$9,739
4CO1019Nat gas JanCommoditiesAmericas7,467$210.07$1,568,593+3.66%HedgedT. Grant08 Jul 2026Yes$57,410
5CO1024Brent DecCommoditiesAPAC3,400$442.48$1,504,432+3.70%OpenT. Grant14 Aug 2026Yes$55,664
6CO1029Nat gas JanCommoditiesAPAC8,121$138.04$1,121,023-2.28%HedgedR. Patel18 Aug 2026Yes-$25,559
7CO1034Gold spotCommoditiesAmericas2,759$341.41$941,950-1.21%OpenL. Chen12 Aug 2026Yes-$11,398
8CO1039Wheat MarCommoditiesEMEA5,633$389.17$2,192,195+2.46%BreachM. Okafor11 Jul 2026No$53,928
9CO1044Brent DecCommoditiesAmericas4,972$76.92$382,446+3.05%BreachN. Ellis02 Aug 2026Yes$11,665
10CO1049Wheat MarCommoditiesAmericas7,854$266.08$2,089,792+1.47%OpenS. Rivera14 Jul 2026Yes$30,720
11CO1054Nat gas JanCommoditiesAmericas8,044$373.06$3,000,895+3.75%OpenL. Chen12 Aug 2026Yes$112,534
12CO1059Nat gas JanCommoditiesAmericas6,867$25.90$177,855+0.08%BreachS. Rivera17 Jul 2026Yes$142.00
13CO1064Wheat MarCommoditiesEMEA174$127.31$22,152+1.96%OpenS. Rivera25 Aug 2026Yes$434.00
14CO1069Nat gas JanCommoditiesEMEA4,792$163.37$782,869-1.28%OpenA. Hart22 Sept 2026No-$10,021
15CO1074Wheat MarCommoditiesAmericas1,556$337.31$524,854-3.72%ReviewL. Chen19 Aug 2026Yes-$19,525
16CO1079Wheat MarCommoditiesAPAC6,206$426.32$2,645,742-1.26%OpenA. Hart15 Jul 2026Yes-$33,336
17CO1084Brent DecCommoditiesEMEA7,619$498.42$3,797,462-0.64%BreachT. Grant07 Jul 2026Yes-$24,304
18CO1089Gold spotCommoditiesEMEA8,412$80.64$678,344-3.31%HedgedN. Ellis21 Aug 2026No-$22,453
19CO1094Copper 3MCommoditiesAPAC2,647$157.37$416,558+0.88%OpenR. Patel22 Sept 2026Yes$3,666
20CO1099Copper 3MCommoditiesAPAC1,041$257.53$268,089-1.12%OpenN. Ellis04 Aug 2026No-$3,003
21CO1104Nat gas JanCommoditiesAmericas2,361$224.09$529,076+2.23%OpenA. Hart07 Sept 2026No$11,798
22CO1109Gold spotCommoditiesEMEA51$120.55$6,148-1.33%OpenM. Okafor03 Aug 2026Yes-$82.00
23CO1114Copper 3MCommoditiesEMEA369$457.55$168,836+1.98%OpenP. Singh14 Aug 2026Yes$3,343
24CO1119Gold spotCommoditiesAPAC4,106$28.47$116,898+3.87%HedgedP. Singh21 Aug 2026Yes$4,524
25CO1124Wheat MarCommoditiesEMEA8,632$393.11$3,393,326+0.83%ReviewM. Okafor15 Sept 2026No$28,165
26CO1129Gold spotCommoditiesEMEA3,705$277.31$1,027,434+2.54%ReviewR. Patel17 Aug 2026Yes$26,097
27CO1134Nat gas JanCommoditiesEMEA7,541$405.48$3,057,725+2.74%ReviewN. Ellis26 Aug 2026Yes$83,782
28CO1139Copper 3MCommoditiesAPAC8,465$450.49$3,813,398+2.92%OpenN. Ellis25 Jul 2026Yes$111,351
29CO1144Wheat MarCommoditiesAmericas4,838$158.83$768,420-3.98%OpenR. Patel17 Jul 2026No-$30,583
30CO1149Nat gas JanCommoditiesAPAC8,829$94.31$832,663-1.52%OpenS. Rivera04 Aug 2026Yes-$12,656
31CO1154Brent DecCommoditiesAPAC8,428$454.11$3,827,239+2.61%BreachP. Singh12 Sept 2026Yes$99,891
32CO1159Gold spotCommoditiesAmericas6,768$484.02$3,275,847-1.64%OpenR. Patel28 Sept 2026Yes-$53,724
33CO1164Wheat MarCommoditiesEMEA8,698$219.63$1,910,342-0.11%OpenS. Rivera26 Jul 2026Yes-$2,101
34CO1169Nat gas JanCommoditiesEMEA1,848$13.05$24,116-1.80%BreachS. Rivera08 Aug 2026Yes-$434.00
35CO1174Brent DecCommoditiesAPAC9,065$232.99$2,112,054+3.40%HedgedP. Singh11 Aug 2026Yes$71,810
36CO1179Brent DecCommoditiesAmericas5,222$24.89$129,976-1.59%OpenR. Patel18 Sept 2026Yes-$2,067
37CO1184Nat gas JanCommoditiesEMEA6,595$126.13$831,827+2.78%OpenL. Chen17 Jul 2026Yes$23,125
38CO1189Gold spotCommoditiesAmericas7,909$473.76$3,746,968+3.69%HedgedN. Ellis14 Aug 2026No$138,263
39CO1194Gold spotCommoditiesEMEA7,542$378.16$2,852,083-2.27%OpenT. Grant09 Jul 2026Yes-$64,742
40CO1199Gold spotCommoditiesAmericas8,677$458.45$3,977,971-3.03%OpenN. Ellis13 Sept 2026Yes-$120,533
41CO1204Nat gas JanCommoditiesAPAC3,380$96.08$324,750-0.36%ReviewA. Hart11 Jul 2026Yes-$1,169
42CO1209Copper 3MCommoditiesAPAC8,480$207.89$1,762,907+1.41%HedgedL. Chen21 Aug 2026Yes$24,857
43CO1214Wheat MarCommoditiesEMEA1,395$189.19$263,920+3.32%OpenP. Singh13 Sept 2026Yes$8,762
44CO1219Brent DecCommoditiesAmericas1,121$401.91$450,541-0.73%OpenN. Ellis10 Jul 2026Yes-$3,289
45CO1224Nat gas JanCommoditiesAPAC2,793$283.24$791,089-2.35%OpenP. Singh11 Sept 2026No-$18,591
46CO1229Brent DecCommoditiesEMEA2,582$161.50$416,993+2.17%HedgedA. Hart03 Jul 2026No$9,049
47CO1234Gold spotCommoditiesAPAC6,255$466.49$2,917,895-1.95%OpenN. Ellis16 Aug 2026Yes-$56,899
48CO1239Brent DecCommoditiesEMEA3,698$254.55$941,326-3.71%OpenL. Chen03 Sept 2026No-$34,923
49CO1244Brent DecCommoditiesAPAC8,295$40.74$337,938+0.51%HedgedA. Hart13 Sept 2026Yes$1,723
50CO1249Copper 3MCommoditiesEMEA9,771$437.66$4,276,376-0.46%OpenR. Patel15 Aug 2026No-$19,671
Σ Total12,660,189$252.42$3,198,811,590+0.07%28 Sept 20262,500-$1,387,689
13 columns · 5 groupsReady
Recent editsDouble-click a cell, type over it, or paste a block from a spreadsheet.

Examples

Database-style account table

Column filters use typed controls: date columns open a date range, number columns use min and max, and select columns use checklists. Applied filters show in both the header and the toolbar.

Customer accounts

15 of 24 accounts

Status: Active, TrialRenewal: 2026-07-01 to 2026-09-30
ABCDEFGHIJKL
#

Filter Account

Filter Status

Values

Filter Plan

Values

Filter Owner

Filter Seats

Filter ARR

Filter Health

Values

Filter Region

Values

Filter Renewal

Filter Active users

Filter Verified

Filter Updated

1Anchor CloudActiveEnterpriseSofia Jensen264£310,000StrongNA31 Jul 2026240Yes11 Jun 2026
2Orchid HealthActiveEnterpriseLaura Chen232£268,000StrongNA22 Sept 2026211Yes11 Jun 2026
3Forge RoboticsActiveEnterprisePriya Singh188£224,500StrongAPAC04 Sept 2026169Yes10 Jun 2026
4Mercury MediaActiveEnterpriseCaleb Stone176£201,300StrongNA09 Jul 2026151Yes09 Jun 2026
5Northstar AnalyticsActiveEnterpriseAmelia Hart142£184,000StrongEMEA19 Aug 2026118Yes10 Jun 2026
6Signal HarborActiveScaleNora Ellis96£112,900StrongEMEA02 Aug 202688Yes07 Jun 2026
7Atlas FinanceActiveScaleRavi Patel84£96,500WatchNA28 Jul 202662Yes08 Jun 2026
8Sable SecurityActiveScaleElena Rossi72£88,900StrongEMEA29 Sept 202670Yes02 Jun 2026
9Violet ManufacturingTrialScaleKai Foster58£65,400WatchAPAC24 Aug 202643No01 Jun 2026
10Summit GroceryActiveGrowthMaeve Walsh36£41,800StrongNA16 Sept 202634Yes06 Jun 2026
11Bluejay TravelTrialGrowthTara Lewis22£24,800WatchLATAM19 Jul 202617No04 Jun 2026
12Brightwell StudioTrialGrowthMina Okafor18£21,400StrongEMEA14 Sept 202616No09 Jun 2026
13Mosaic CivicActiveStarterGeorge White16£15,800StrongEMEA11 Sept 202615Yes10 Jun 2026
14Kite EducationTrialStarterOwen Price14£11,800StrongEMEA31 Aug 202613No06 Jun 2026
15Pioneer RetailTrialStarterMiles Brooks9£7,800WatchNA12 Jul 20266No03 Jun 2026
12 columns2 selected

Resource API shape

The grid can filter locally today, but the same query-change payload can be sent to a resource endpoint for SQL-backed search, where, order, and pagination later.

{
  "search": "",
  "filters": {
    "status": {
      "type": "select",
      "values": [
        "active",
        "trial"
      ]
    },
    "renewalDate": {
      "type": "date",
      "start": "2026-07-01",
      "end": "2026-09-30"
    }
  },
  "sort": {
    "key": "arr",
    "direction": "desc"
  }
}

Examples

Flush page edges

layout="flush" with gutter-class="px-4 sm:px-6" lines the toolbar, footer and outer cells up with the page gutter.

Articles

Article library

2 of 2 rows

Designing a useful content libraryDraft1,200
Keeping content alignedPublished860
3 columnsReady

Examples

Virtual rows for in-memory data

10,000 rows held in memory. Search, filters and sorting cover every row, and only the visible rows are mounted.

Usage events

10,000 of 10,000 events

ABCDEFGH
#

Filter Event

Filter Account

Filter Service

Values

Filter Status

Values

Filter Region

Values

Filter Requests

Filter Spend

Filter Updated

11Atlas Works 1Billing APIHealthyUK2,500£150.0001 Jan 2026
22Northstar Labs 1MailDelayedEU2,597£187.0002 Feb 2026
33Pioneer Health 1Billing APIQueuedUS2,694£224.0003 Mar 2026
44Evergreen Finance 1MailHealthyCA2,791£261.0004 Apr 2026
55Lunar Retail 1Billing APIHealthyAU2,888£298.0005 May 2026
66Cobalt Systems 1MailFailedUK2,985£335.0006 Jun 2026
77Atlas Works 2Billing APIHealthyEU3,082£372.0007 Jan 2026
88Northstar Labs 2MailDelayedUS3,179£409.0008 Feb 2026
99Pioneer Health 2Billing APIQueuedCA3,276£446.0009 Mar 2026
1010Evergreen Finance 2MailHealthyAU3,373£483.0010 Apr 2026
1111Lunar Retail 2Billing APIHealthyUK3,470£520.0011 May 2026
1212Cobalt Systems 2MailFailedEU3,567£557.0012 Jun 2026
1313Atlas Works 3Billing APIHealthyUS3,664£594.0013 Jan 2026
1414Northstar Labs 3MailDelayedCA3,761£631.0014 Feb 2026
1515Pioneer Health 3Billing APIQueuedAU3,858£668.0015 Mar 2026
1616Evergreen Finance 3MailHealthyUK3,955£705.0016 Apr 2026
1717Lunar Retail 3Billing APIHealthyEU4,052£742.0017 May 2026
1818Cobalt Systems 3MailFailedUS4,149£779.0018 Jun 2026
1919Atlas Works 4Billing APIHealthyCA4,246£816.0019 Jan 2026
2020Northstar Labs 4MailDelayedAU4,343£853.0020 Feb 2026
2121Pioneer Health 4Billing APIQueuedUK4,440£890.0021 Mar 2026
2222Evergreen Finance 4MailHealthyEU4,537£927.0022 Apr 2026
2323Lunar Retail 4Billing APIHealthyUS4,634£964.0023 May 2026
2424Cobalt Systems 4MailFailedCA4,731£1,00124 Jun 2026
2525Atlas Works 5Billing APIHealthyAU4,828£1,03825 Jan 2026
2626Northstar Labs 5MailDelayedUK4,925£1,07526 Feb 2026
2727Pioneer Health 5Billing APIQueuedEU5,022£1,11227 Mar 2026
2828Evergreen Finance 5MailHealthyUS5,119£1,14928 Apr 2026
2929Lunar Retail 5Billing APIHealthyCA5,216£1,18601 May 2026
3030Cobalt Systems 5MailFailedAU5,313£1,22302 Jun 2026
3131Atlas Works 6Billing APIHealthyUK5,410£1,26003 Jan 2026
3232Northstar Labs 6MailDelayedEU5,507£1,29704 Feb 2026
3333Pioneer Health 6Billing APIQueuedUS5,604£1,33405 Mar 2026
3434Evergreen Finance 6MailHealthyCA5,701£1,37106 Apr 2026
3535Lunar Retail 6Billing APIHealthyAU5,798£1,40807 May 2026
3636Cobalt Systems 6MailFailedUK5,895£1,44508 Jun 2026
3737Atlas Works 7Billing APIHealthyEU5,992£1,48209 Jan 2026
3838Northstar Labs 7MailDelayedUS6,089£1,51910 Feb 2026
3939Pioneer Health 7Billing APIQueuedCA6,186£1,55611 Mar 2026
4040Evergreen Finance 7MailHealthyAU6,283£1,59312 Apr 2026
4141Lunar Retail 7Billing APIHealthyUK6,380£1,63013 May 2026
4242Cobalt Systems 7MailFailedEU6,477£1,66714 Jun 2026
4343Atlas Works 8Billing APIHealthyUS6,574£1,70415 Jan 2026
4444Northstar Labs 8MailDelayedCA6,671£1,74116 Feb 2026
4545Pioneer Health 8Billing APIQueuedAU6,768£1,77817 Mar 2026
4646Evergreen Finance 8MailHealthyUK6,865£1,81518 Apr 2026
4747Lunar Retail 8Billing APIHealthyEU6,962£1,85219 May 2026
4848Cobalt Systems 8MailFailedUS7,059£1,88920 Jun 2026
4949Atlas Works 9Billing APIHealthyCA7,156£1,92621 Jan 2026
5050Northstar Labs 9MailDelayedAU7,253£1,96322 Feb 2026
5151Pioneer Health 9Billing APIQueuedUK7,350£2,00023 Mar 2026
8 columnsReady

Examples

Keyword table sort presets

multi-sort turns header clicks into ordered sort rules. Click Volume, then Difficulty, to sort by both.

Keyword opportunity sort

The selected preset sends ordered sort rules, so APIs can translate them to stable ORDER BY clauses.

Keyword research

10 of 10 keywords

ABCDEF
#

Filter Keyword

Filter Intent

Values

Filter Volume

Filter Difficulty

Filter Opportunity

Filter CPC

1vue data gridCommercial5,400283,942$5.40
2javascript spreadsheet componentComparison5,400433,132$7.10
3editable table vueCommercial2,900212,320$4.80
4tailwind data tableInformational8,100613,240$3.90
5server side data gridCommercial3,600352,376$8.20
6open source data grid vueComparison4,400243,388$4.60
7keyword clustering toolTransactional6,600483,498$6.70
8saas dashboard tableInformational1,900171,596$3.20
9best vue table libraryComparison5,400313,780$5.90
10crm data gridCommercial2,400191,968$9.40
6 columnsReady
{
  "search": "",
  "sort": {
    "key": "",
    "direction": "",
    "rules": []
  }
}

Examples

Server pagination and forever scrolling

One resource endpoint serves numbered pages and cursor-based scrolling. The grid sends search, filters and sort; the app adds paging.

Remote customer resource

The grid asks for rows; the mock API applies search, filters, sort, and pagination.

Customers

0 of 0 customers from resource API

ABCDEFG
#

Filter Customer

Filter Status

Values

Filter Owner

Filter Region

Values

Filter Seats

Filter ARR

Filter Updated

No rows match the current filters.
7 columnsReady

Request

{
  "resource": "customers",
  "query": {
    "search": "",
    "filters": {},
    "sort": {
      "key": "updatedAt",
      "direction": "desc"
    }
  },
  "pagination": {
    "strategy": "page",
    "page": 1,
    "pageSize": 8
  }
}

Response

{
  "data": [],
  "pageInfo": {
    "strategy": "page",
    "page": 1,
    "pageSize": 8,
    "pageCount": 1,
    "total": 0,
    "hasNextPage": false,
    "hasPreviousPage": false,
    "nextCursor": ""
  },
  "meta": {
    "total": 0,
    "returned": 0
  }
}

Examples

Live SQL query log

New rows stream into a server-backed grid while search, filters and a duration_ms desc sort keep the slowest statements on top.

Live query log 4-9/sec Last event 21:35:43

Buffered

140

P95 duration

936 ms

Slow queries

36

Errors

11

SQL log

80 of 140 queries from resource API

#

Filter Database

Values

Filter Service

Values

Filter Verb

Values

Filter Table

Values

Filter Duration ms

Filter Lock wait

Filter Rows examined

Filter Status

Values

Filter Plan

Values

Filter Endpoint

Filter SQL statement

121:33:56Primary writerBilling syncINSERTfeature_flags1484 ms0 ms396TimeoutIndex scanstripe:insertinsert into feature_flags (organisation_id, payload, created_at) values (?, ?, now()) returning id
221:34:28BillingBilling syncUPDATEusers1452 ms0 ms918SlowIndex scanstripe:updateupdate users set updated_at = now(), metadata = jsonb_set(metadata, ?, ?) where organisation_id = ? and id = ?
321:34:25AnalyticsWorkerINSERTaccounts1440 ms143 ms1,141ErrorIndex scanjob:accounts:syncinsert into accounts (organisation_id, payload, created_at) values (?, ?, now()) returning id
421:30:02AnalyticsWebhookSELECTfeature_flags1406 ms615 ms154,328TimeoutSeq scan/webhooks/feature_flagsselect * from feature_flags where organisation_id = ? and lower(search_text) like ? order by updated_at desc limit 100
521:32:53BillingWorkerSELECTsessions1322 ms0 ms29,536SlowSeq scanjob:sessions:syncselect * from sessions where organisation_id = ? and lower(search_text) like ? order by updated_at desc limit 100
621:32:55Primary writerBilling syncINSERTaudit_events1177 ms0 ms6SlowIndex scanstripe:insertinsert into audit_events (organisation_id, payload, created_at) values (?, ?, now()) returning id
721:34:45AnalyticsBilling syncUPDATEaudit_events1149 ms0 ms345SlowIndex scanstripe:updateupdate audit_events set updated_at = now(), metadata = jsonb_set(metadata, ?, ?) where organisation_id = ? and id = ?
821:30:47Primary writerWebhookSELECTcontent_items936 ms0 ms635,204SlowSeq scan/webhooks/content_itemsselect * from content_items where organisation_id = ? and lower(search_text) like ? order by updated_at desc limit 100
921:35:40BillingWorkerDELETEfeature_flags885 ms0 ms139SlowIndex scanjob:feature_flags:syncdelete from feature_flags where organisation_id = ? and archived_at < now() - interval '90 days'
1021:31:05AnalyticsApp APISELECTaccounts867 ms0 ms764,861SlowSeq scan/api/accountsselect * from accounts where organisation_id = ? and lower(search_text) like ? order by updated_at desc limit 100
1121:30:17BillingWorkerSELECTwebsite_pages852 ms360 ms92,493SlowNested loopjob:website_pages:syncselect * from website_pages where organisation_id = ? and lower(search_text) like ? order by updated_at desc limit 100
1221:32:21Read replicaReportingSELECTbilling_events772 ms0 ms592,246SlowSeq scan/reports/billing_eventsselect * from billing_events where organisation_id = ? and lower(search_text) like ? order by updated_at desc limit 100
1321:34:58BillingReportingSELECTorganisations732 ms0 ms318,401SlowSeq scan/reports/organisationsselect * from organisations where organisation_id = ? and lower(search_text) like ? order by updated_at desc limit 100
1421:35:32AnalyticsWorkerSELECTinvoices691 ms0 ms509,075SlowSeq scanjob:invoices:syncselect * from invoices where organisation_id = ? and lower(search_text) like ? order by updated_at desc limit 100
1521:28:06BillingApp APIDELETEbilling_events658 ms73 ms742SlowNested loop/api/billing_eventsdelete from billing_events where organisation_id = ? and archived_at < now() - interval '90 days'
1621:27:46AnalyticsWebhookSELECTfeature_flags564 ms0 ms570,307SlowSeq scan/webhooks/feature_flagsselect * from feature_flags where organisation_id = ? and lower(search_text) like ? order by updated_at desc limit 100
1721:29:49Primary writerApp APISELECTinvoices539 ms0 ms374,807SlowSeq scan/api/invoicesselect * from invoices where organisation_id = ? and lower(search_text) like ? order by updated_at desc limit 100
1821:33:10AnalyticsApp APIINSERTaudit_events529 ms0 ms698SlowIndex scan/api/audit_eventsinsert into audit_events (organisation_id, payload, created_at) values (?, ?, now()) returning id
1921:35:22BillingWebhookSELECTaccounts516 ms0 ms117,535SlowSeq scan/webhooks/accountsselect * from accounts where organisation_id = ? and lower(search_text) like ? order by updated_at desc limit 100
2021:31:42AnalyticsReportingUPDATEcontent_items468 ms0 ms434SlowIndex scan/reports/content_itemsupdate content_items set updated_at = now(), metadata = jsonb_set(metadata, ?, ?) where organisation_id = ? and id = ?
2121:31:49AnalyticsReportingSELECTusers432 ms0 ms726,742SlowSeq scan/reports/usersselect * from users where organisation_id = ? and lower(search_text) like ? order by updated_at desc limit 100
2221:31:00Read replicaReportingSELECTwebsite_pages364 ms0 ms267,545SlowSeq scan/reports/website_pagesselect * from website_pages where organisation_id = ? and lower(search_text) like ? order by updated_at desc limit 100
2321:23:58AnalyticsBilling syncSELECTorganisations328 ms0 ms398,727SlowSeq scanstripe:selectselect * from organisations where organisation_id = ? and lower(search_text) like ? order by updated_at desc limit 100
2421:30:51Read replicaBilling syncINSERTusers316 ms0 ms63SlowNested loopstripe:insertinsert into users (organisation_id, payload, created_at) values (?, ?, now()) returning id
2521:33:45BillingBilling syncUPDATEjobs305 ms25 ms1,102SlowIndex scanstripe:updateupdate jobs set updated_at = now(), metadata = jsonb_set(metadata, ?, ?) where organisation_id = ? and id = ?
2621:35:33Read replicaWebhookINSERTinvoices289 ms0 ms727SlowIndex scan/webhooks/invoicesinsert into invoices (organisation_id, payload, created_at) values (?, ?, now()) returning id
2721:32:05AnalyticsReportingUPDATEfeature_flags289 ms0 ms85SlowNested loop/reports/feature_flagsupdate feature_flags set updated_at = now(), metadata = jsonb_set(metadata, ?, ?) where organisation_id = ? and id = ?
2821:32:28BillingBilling syncINSERTusers286 ms0 ms799ErrorIndex scanstripe:insertinsert into users (organisation_id, payload, created_at) values (?, ?, now()) returning id
2921:33:26Primary writerWorkerUPDATEfeature_flags280 ms19 ms101SlowIndex scanjob:feature_flags:syncupdate feature_flags set updated_at = now(), metadata = jsonb_set(metadata, ?, ?) where organisation_id = ? and id = ?
3021:31:40Primary writerWorkerINSERTinvoices279 ms0 ms853SlowNested loopjob:invoices:syncinsert into invoices (organisation_id, payload, created_at) values (?, ?, now()) returning id
3121:32:12Primary writerApp APIINSERTfeature_flags275 ms0 ms1,066SlowIndex scan/api/feature_flagsinsert into feature_flags (organisation_id, payload, created_at) values (?, ?, now()) returning id
3221:33:01BillingWebhookINSERTorganisations269 ms0 ms733SlowIndex scan/webhooks/organisationsinsert into organisations (organisation_id, payload, created_at) values (?, ?, now()) returning id
3321:29:49Read replicaApp APIUPDATEaccounts268 ms0 ms641SlowIndex scan/api/accountsupdate accounts set updated_at = now(), metadata = jsonb_set(metadata, ?, ?) where organisation_id = ? and id = ?
3421:31:27AnalyticsBilling syncINSERTsessions258 ms0 ms807SlowIndex scanstripe:insertinsert into sessions (organisation_id, payload, created_at) values (?, ?, now()) returning id
3521:34:50BillingApp APIINSERTsessions254 ms0 ms551SlowIndex scan/api/sessionsinsert into sessions (organisation_id, payload, created_at) values (?, ?, now()) returning id
3621:28:19Read replicaApp APIDELETEwebsite_pages254 ms52 ms599SlowIndex scan/api/website_pagesdelete from website_pages where organisation_id = ? and archived_at < now() - interval '90 days'
3721:31:07AnalyticsWorkerINSERTusers227 ms43 ms994OKIndex scanjob:users:syncinsert into users (organisation_id, payload, created_at) values (?, ?, now()) returning id
3821:26:01Primary writerBilling syncINSERTwebsite_pages201 ms0 ms677OKIndex scanstripe:insertinsert into website_pages (organisation_id, payload, created_at) values (?, ?, now()) returning id
3921:33:57AnalyticsApp APIINSERTusers200 ms0 ms216OKIndex scan/api/usersinsert into users (organisation_id, payload, created_at) values (?, ?, now()) returning id
4021:35:37Primary writerWebhookUPDATEaudit_events197 ms0 ms289OKIndex scan/webhooks/audit_eventsupdate audit_events set updated_at = now(), metadata = jsonb_set(metadata, ?, ?) where organisation_id = ? and id = ?
4121:35:08BillingBilling syncUPDATEcontent_items190 ms0 ms271OKIndex scanstripe:updateupdate content_items set updated_at = now(), metadata = jsonb_set(metadata, ?, ?) where organisation_id = ? and id = ?
4221:33:33AnalyticsBilling syncSELECTbilling_events189 ms0 ms5,696OKIndex scanstripe:selectselect id, organisation_id, status, updated_at from billing_events where organisation_id = ? and id = ? limit 1
4321:25:19Primary writerBilling syncSELECTjobs189 ms0 ms5,873OKIndex scanstripe:selectselect id, organisation_id, status, updated_at from jobs where organisation_id = ? and id = ? limit 1
4421:30:56BillingBilling syncSELECTorganisations186 ms0 ms6,275OKIndex scanstripe:selectselect id, organisation_id, status, updated_at from organisations where organisation_id = ? and id = ? limit 1
4521:28:17Primary writerApp APISELECTjobs186 ms0 ms9,195OKIndex scan/api/jobsselect id, organisation_id, status, updated_at from jobs where organisation_id = ? and id = ? limit 1
4621:32:54Read replicaWebhookSELECTbilling_events184 ms0 ms4,919OKIndex scan/webhooks/billing_eventsselect id, organisation_id, status, updated_at from billing_events where organisation_id = ? and id = ? limit 1
4721:30:43Primary writerBilling syncSELECTinvoices182 ms0 ms5,707OKIndex scanstripe:selectselect id, organisation_id, status, updated_at from invoices where organisation_id = ? and id = ? limit 1
4821:33:58BillingReportingSELECTusers181 ms0 ms3,325OKIndex scan/reports/usersselect id, organisation_id, status, updated_at from users where organisation_id = ? and id = ? limit 1
4921:31:49AnalyticsWebhookSELECTaudit_events181 ms0 ms3,685OKIndex scan/webhooks/audit_eventsselect id, organisation_id, status, updated_at from audit_events where organisation_id = ? and id = ? limit 1
5021:31:54AnalyticsApp APISELECTjobs176 ms0 ms3,588OKIndex scan/api/jobsselect id, organisation_id, status, updated_at from jobs where organisation_id = ? and id = ? limit 1
5121:34:37Primary writerApp APISELECTaccounts175 ms0 ms3,508ErrorIndex scan/api/accountsselect id, organisation_id, status, updated_at from accounts where organisation_id = ? and id = ? limit 1
5221:34:41BillingWorkerSELECTbilling_events170 ms0 ms2,890OKIndex scanjob:billing_events:syncselect id, organisation_id, status, updated_at from billing_events where organisation_id = ? and id = ? limit 1
5321:34:48AnalyticsApp APISELECTcontent_items168 ms0 ms136OKIndex scan/api/content_itemsselect id, organisation_id, status, updated_at from content_items where organisation_id = ? and id = ? limit 1
5421:31:10AnalyticsWorkerINSERTwebsite_pages166 ms0 ms301OKIndex scanjob:website_pages:syncinsert into website_pages (organisation_id, payload, created_at) values (?, ?, now()) returning id
5521:25:50Read replicaReportingSELECTaudit_events165 ms0 ms5,001OKIndex scan/reports/audit_eventsselect id, organisation_id, status, updated_at from audit_events where organisation_id = ? and id = ? limit 1
5621:33:38BillingWebhookINSERTaccounts164 ms0 ms895OKIndex scan/webhooks/accountsinsert into accounts (organisation_id, payload, created_at) values (?, ?, now()) returning id
5721:27:47Read replicaReportingSELECTaudit_events163 ms0 ms1,638ErrorIndex scan/reports/audit_eventsselect id, organisation_id, status, updated_at from audit_events where organisation_id = ? and id = ? limit 1
5821:35:21BillingWorkerUPDATEinvoices162 ms0 ms495OKIndex scanjob:invoices:syncupdate invoices set updated_at = now(), metadata = jsonb_set(metadata, ?, ?) where organisation_id = ? and id = ?
5921:35:11Read replicaReportingSELECTwebsite_pages161 ms0 ms8,303OKIndex scan/reports/website_pagesselect id, organisation_id, status, updated_at from website_pages where organisation_id = ? and id = ? limit 1
6021:34:27Primary writerWorkerSELECTjobs159 ms0 ms6,274OKIndex scanjob:jobs:syncselect id, organisation_id, status, updated_at from jobs where organisation_id = ? and id = ? limit 1
6121:32:53Read replicaWorkerSELECTbilling_events159 ms0 ms2,077OKIndex scanjob:billing_events:syncselect id, organisation_id, status, updated_at from billing_events where organisation_id = ? and id = ? limit 1
6221:32:33AnalyticsBilling syncSELECTorganisations158 ms0 ms8,514OKIndex scanstripe:selectselect id, organisation_id, status, updated_at from organisations where organisation_id = ? and id = ? limit 1
6321:34:19BillingWorkerSELECTwebsite_pages153 ms0 ms4,938OKIndex scanjob:website_pages:syncselect id, organisation_id, status, updated_at from website_pages where organisation_id = ? and id = ? limit 1
6421:30:02BillingWebhookUPDATEorganisations152 ms0 ms782OKIndex scan/webhooks/organisationsupdate organisations set updated_at = now(), metadata = jsonb_set(metadata, ?, ?) where organisation_id = ? and id = ?
6521:35:00Read replicaBilling syncSELECTusers148 ms0 ms7,719OKIndex scanstripe:selectselect id, organisation_id, status, updated_at from users where organisation_id = ? and id = ? limit 1
6621:31:38Primary writerWorkerSELECTorganisations148 ms0 ms6,600OKIndex scanjob:organisations:syncselect id, organisation_id, status, updated_at from organisations where organisation_id = ? and id = ? limit 1
6721:35:14Read replicaApp APISELECTfeature_flags144 ms0 ms7,576OKIndex scan/api/feature_flagsselect id, organisation_id, status, updated_at from feature_flags where organisation_id = ? and id = ? limit 1
6821:30:24Primary writerWebhookDELETEwebsite_pages144 ms0 ms547OKNested loop/webhooks/website_pagesdelete from website_pages where organisation_id = ? and archived_at < now() - interval '90 days'
6921:30:51BillingWebhookUPDATEfeature_flags141 ms0 ms505OKNested loop/webhooks/feature_flagsupdate feature_flags set updated_at = now(), metadata = jsonb_set(metadata, ?, ?) where organisation_id = ? and id = ?
7021:35:35BillingWebhookSELECTinvoices140 ms0 ms5,940OKIndex scan/webhooks/invoicesselect id, organisation_id, status, updated_at from invoices where organisation_id = ? and id = ? limit 1
7121:34:50Primary writerApp APISELECTaccounts137 ms0 ms1,347OKIndex scan/api/accountsselect id, organisation_id, status, updated_at from accounts where organisation_id = ? and id = ? limit 1
7221:33:04Read replicaWebhookSELECTaudit_events135 ms0 ms8,683OKIndex scan/webhooks/audit_eventsselect id, organisation_id, status, updated_at from audit_events where organisation_id = ? and id = ? limit 1
7321:29:45Primary writerWorkerINSERTinvoices132 ms48 ms82OKNested loopjob:invoices:syncinsert into invoices (organisation_id, payload, created_at) values (?, ?, now()) returning id
7421:31:44AnalyticsWebhookSELECTwebsite_pages130 ms0 ms8,042OKIndex scan/webhooks/website_pagesselect id, organisation_id, status, updated_at from website_pages where organisation_id = ? and id = ? limit 1
7521:35:22BillingReportingSELECTinvoices125 ms0 ms6,802OKIndex scan/reports/invoicesselect id, organisation_id, status, updated_at from invoices where organisation_id = ? and id = ? limit 1
7621:31:12Read replicaApp APISELECTinvoices124 ms0 ms8,772ErrorIndex scan/api/invoicesselect id, organisation_id, status, updated_at from invoices where organisation_id = ? and id = ? limit 1
7721:31:09Read replicaReportingUPDATEsessions124 ms0 ms1,069ErrorIndex scan/reports/sessionsupdate sessions set updated_at = now(), metadata = jsonb_set(metadata, ?, ?) where organisation_id = ? and id = ?
7821:34:45Read replicaBilling syncUPDATEaudit_events122 ms0 ms555OKNested loopstripe:updateupdate audit_events set updated_at = now(), metadata = jsonb_set(metadata, ?, ?) where organisation_id = ? and id = ?
7921:29:02AnalyticsBilling syncSELECTsessions122 ms0 ms2,258OKIndex scanstripe:selectselect id, organisation_id, status, updated_at from sessions where organisation_id = ? and id = ? limit 1
8021:30:19BillingApp APISELECTorganisations121 ms0 ms5,347OKIndex scan/api/organisationsselect id, organisation_id, status, updated_at from organisations where organisation_id = ? and id = ? limit 1
12 columnsReady

SQL generated from grid state

select
	id, started_at, database_name, service_name, operation, table_name,
	duration_ms, lock_wait_ms, rows_examined, status, plan, endpoint, statement
from observability.query_log
order by duration_ms DESC
limit 80;

Request

{
  "resource": "observability.query_log",
  "query": {
    "search": "",
    "filters": {},
    "sort": {
      "key": "durationMs",
      "direction": "desc",
      "rules": [
        {
          "key": "durationMs",
          "direction": "desc"
        }
      ]
    }
  },
  "pagination": {
    "strategy": "live-window",
    "limit": 80,
    "buffered": 140
  }
}

Response window

{
  "returned": 80,
  "total": 140,
  "buffered": 140,
  "lastUpdatedAt": "21:35:43"
}

Examples

Column components and row actions

column.component renders every cell in a column with your component, here an inline name editor and a row actions menu.

Team members

6 of 6 users

Filter User

Filter Team

Filter Status

Values

Filter Last active

ProductPending17 Jun 2026
EngineeringActive16 Jun 2026
DesignActive12 Jun 2026
SupportPaused08 Jun 2026
OperationsPending06 Jun 2026
SalesActive04 Jun 2026
5 columnsReady

EditNameCell.vue

EditNameCell.vuevue

RowActionsCell.vue

RowActionsCell.vuevue

Reference

Props

Control props

NameTypeTSDefaultDescription
layout'card' | 'flush'string'card'Card keeps the bordered container. Flush removes outer side borders, rounding and shadow; the application owns the surrounding width.
gutterClassstringstring'px-3'Horizontal padding classes for flush toolbar, footer and outer cell edges, such as px-4 sm:px-6 or px-[var(--page-gutter)]. Ignored in card layout.
rowsarrayArray<unknown>[]Rows to render. Each row should be a plain object from an API, store, or database result.
columnsarrayArray<DataGridColumn
type DataGridColumn = {
	key: string; // Stable column id. It can match a row property, or identify a display-only column such as actions.
	field?: string; // Row property to read when it differs from key.
	label?: string; // Header label. Defaults to the key in title case.
	type?: 'text' | 'number' | 'date' | 'boolean' | 'select' | 'currency'; // Column data type. Drives sorting, formatting, alignment, the filter control and the editor. Defaults to 'text'.
	width?: string | number; // CSS width for the column. Numbers are pixels. Defaults by type: 13rem text, 7rem number and boolean, 8.5rem currency, 9.5rem date, 9rem select.
	align?: 'left' | 'center' | 'right'; // Cell alignment. Defaults to right for number and currency, center for boolean, left otherwise.
	component?: DomVueComponent | string; // Vue component used to render cells in this column. Receives row, column, value, formatted, rowIndex and rowKey props, and may emit update:value or action.
	resizable?: boolean; // Set false to disable drag resizing for this column.
	sortable?: boolean; // Set false to disable sorting.
	filterable?: boolean; // Set false to disable filtering for this column, hide the filter button, and omit the column from query filters.
	searchable?: boolean; // Set false to remove the column from global search.
	hidden?: boolean; // Set true to leave the column out of the grid.
	options?: Array<string | number | boolean | DomOption | Record<string, unknown>>; // Choices for select columns, used by the filter checklist and the editor. Objects may include label, value and tone. Supply them for large data sets; otherwise the grid scans every row to find the choices.
	currency?: string; // ISO currency code for currency columns. Defaults to 'GBP'.
	locale?: string; // Locale used to format numbers, currency and dates in this column. Defaults to 'en-GB'.
	accessor?: (row: Record<string, unknown>, column: DataGridColumn
) => unknown; // Returns the cell value for a row. Use it for computed or nested values. sortAccessor?: (row: Record<string, unknown>, column: DataGridColumn
) => unknown; // Returns the value used for local sorting when it differs from the cell value. format?: (value: unknown, row: Record<string, unknown>, column: DataGridColumn
) => string | number; // Returns the display text for a cell. Also used by global search. pinned?: 'left' | 'right' | boolean; // Pin the column to the left or right edge so it stays visible while the grid scrolls sideways. true pins left. editable?: boolean | ((row: Record<string, unknown>, column: DataGridColumn
) => boolean); // Allow inline editing for this column, or decide per cell. Defaults to the grid editable prop for plain cells. parse?: (text: string, column: DataGridColumn
) => unknown; // Converts typed or pasted text to the value to store. Overrides the built-in parser for the column type. validate?: (value: unknown, row: Record<string, unknown>, column: DataGridColumn
) => true | string; // Return true to accept an edit, or a message to reject it. aggregate?: 'sum' | 'avg' | 'count' | 'min' | 'max' | ((values: Array<unknown>, rows: Array<Record<string, unknown>>, column: DataGridColumn
) => unknown); // Aggregate shown on group rows and the totals row. formatAggregate?: (value: unknown, column: DataGridColumn
, kind: string) => string; // Returns display text for an aggregate value. exportable?: boolean; // Set false to leave the column out of CSV and Excel exports. exportValue?: (value: unknown, row: Record<string, unknown>, column: DataGridColumn
) => unknown; // Returns the exported and copied value for a cell. };
>
[]Column definitions. Each type chooses its own sorting, formatting, and filter controls.
rowKeystring | functionstring'id'Field name or function used to identify each row.
filtersobjectRecord<string, unknown>{}Controlled filter map keyed by column key.
sortobject | arrayArray<unknown>{"key":"","direction":""}Controlled sort state as { key, direction }, or { rules } for multi-column and computed sorting.
multiSortbooleanbooleanfalseWhen true, header clicks toggle each sortable column inside an ordered sort.rules array.
searchstringstring''Global search text.
selectedKeysarrayArray<unknown>[]Selected row keys.
remotebooleanbooleanfalseWhen true, rows render as supplied and query-change can drive a server/API request.
loadingbooleanbooleanfalseShow a loading row while server data is being fetched.
virtualRowsbooleanbooleanfalseRender large in-memory row sets through the isolated virtual row renderer.
virtualRowBuffernumbernumber50Rows kept mounted above and below the visible viewport when virtualRows is true.
virtualRowHeightnumbernumber—Fixed row height in pixels for virtual row calculations. Defaults to the grid density.
selectablebooleanbooleantrueShow row selection checkboxes.
showRowNumbersbooleanbooleantrueShow spreadsheet-style row numbers.
showColumnLettersbooleanbooleantrueShow spreadsheet-style column letters above headers.
stickyHeaderbooleanbooleantrueKeep header rows visible while the grid scrolls.
resizableColumnsbooleanbooleantrueAllow users to drag header dividers to resize columns.
minColumnWidthnumbernumber40Minimum column width in pixels while resizing. The default leaves room for the filter icon.
toolbarbooleanbooleantrueShow the title, search box, filter chips, and row count toolbar.
titlestringstring'Data grid'Toolbar title.
resourceLabelstringstring'rows'Human label for the resource being rendered.
totalRowsnumbernumber—Server-side total row count when remote is true.
density'compact' | 'comfortable'string'compact'Row height density.
heightstring | numberstring'34rem'Scrollable grid height as a CSS size or pixel number.
emptyTextstringstring'No rows match the current filters.'Message shown when no rows are visible.
pinnedColumnsobjectRecord<string, unknown>—Controlled pin map keyed by column key, such as { account: 'left', actions: 'right' }. When omitted, each column's pinned value is used.
groupBystring | arrayArray<unknown>''Column key, or ordered column keys, to group rows by. Group rows show a count and each column aggregate.
groupsExpandedbooleanbooleantrueWhether groups start expanded.
showTotalsbooleanbooleanfalseShow a totals row pinned to the bottom with each column aggregate over the filtered rows.
editablebooleanbooleanfalseAllow inline editing of plain cells. Edits emit cell-update; column editable overrides this per column.
cellSelectionbooleanbooleanfalseSpreadsheet cell focus: arrow-key navigation, drag or Shift range selection, copy, paste, delete and undo.
columnMenubooleanbooleanfalseShow a menu on every header with pin and group actions beside the column filter.
exportablebooleanbooleanfalseShow an Export menu in the toolbar for CSV and Excel (.xlsx) downloads of the filtered, sorted rows.
exportFileNamestringstring''Export file name without an extension. Defaults to the title.

Auto-generated from Data grid.props and inline _edit hints.

Events

NamePayloadDescription
@update:filtersRecord<string, DataGridFilter>Fired when a column filter is applied or cleared.
@filter-change(payload
payload: unknown;
: unknown)
—
@update:sortDataGridSortFired when column sorting changes. Complex sort values may include an ordered rules array.
@sort-change(payload
payload: unknown;
: unknown)
—
@update:searchstringFired when the toolbar search changes.
@query-changeDataGridQueryFired with the normalized search, filters, and sort state. Use this to call a resource API.
@viewport-scroll{ scrollTop, scrollHeight, clientHeight, remaining, atEnd }Fired when the scrollable row viewport scrolls.
@viewport-end{ scrollTop, scrollHeight, clientHeight, remaining, atEnd }Fired when the scrollable row viewport reaches the bottom threshold. Useful for cursor pagination and infinite loading.
@cell-update({ row, column, rowIndex, rowKey, value, previousValue, source })Fired when a cell value changes: an inline edit, paste, delete, undo or redo (source), or a column component emitting update:value. The grid does not mutate rows; write value back to your data.
@cell-action({ row, column, rowIndex, rowKey, value, formatted, action, payload })Re-emitted when a column component emits action.
@column-resize({ column, key, width, previousWidth, startWidth })Fired while a header divider is dragged and a column width changes.
@column-resize-end({ column, key, width, previousWidth, startWidth })Fired when the user finishes resizing a column.
@render-window-change({ startIndex, endIndex, renderedRows, totalRows, firstVisibleIndex, lastVisibleIndex })Fired by the virtual row renderer when the mounted row window changes.
@update:selectedKeysArray<string | number>Fired when row selection changes.
@selection-change(payload
payload: unknown;
: unknown)
—
@row-click({ row, index })Fired when a data row is clicked.
@cells-update({ source, changes })Fired once per edit, paste, delete, undo or redo with every change, after the individual cell-update events. Use it instead of cell-update to apply a batch in one step.
@range-changeDataGridRange | nullFired when the selected cell range changes. Indexes are visible positions.
@update:pinnedColumnsRecord<string, 'left' | 'right'>Fired when a column is pinned or unpinned from the column menu.
@update:groupBystring | Array<string>Fired when grouping changes from the column menu or toolbar.
@export({ format, rows, columns, fileName })Fired after a CSV or Excel export is built.

Names auto-detected from defineEmits and source emit() calls; payload and description from __doc.events when present.

Slots

NameScopeDescription
#cell{ row, column, value, formatted, rowIndex, rowKey }Custom cell renderer.
#toolbar-actions{ query, clearFilters, exportCsv, exportExcel }Extra controls rendered next to search and Clear filters.
#empty—Custom empty state.

Methods

Call these on a template ref.

NameSignatureDescription
exportCsv(options?: { onlySelected?: boolean; fileName?: string; download?: boolean }) => stringDownloads the filtered, sorted rows as CSV and returns the text. Pass download: false to only get the text.
exportExcel(options?: { onlySelected?: boolean; fileName?: string; download?: boolean }) => BlobDownloads the filtered, sorted rows as an Excel workbook with typed cells and returns the file.
getRange() => DataGridRange | nullReturns the selected cell range, or null.
selectRange(range: { startRow: number; endRow?: number; startColumn?: number | string; endColumn?: number | string }) => voidSelects a cell range by visible row position and column index or key.
clearRange() => voidClears the active cell and range.
undo() => booleanUndoes the last edit, paste or delete. Returns false when there is nothing to undo.
redo() => booleanRedoes the last undone change. Returns false when there is nothing to redo.
expandAllGroups() => voidOpens every group.
collapseAllGroups() => voidCloses every group.

Keyboard

  • Enter / Space on a headerToggle sort when the column is sortable.
  • Enter / Space on a filter buttonOpen the typed filter popover.
  • EscClose an open filter popover, cancel an edit, or collapse a range to the active cell.
  • Arrow keys / TabMove the active cell when cellSelection is on. Shift extends the range; Cmd or Ctrl jumps to the edge.
  • Enter / F2 / typingEdit the active cell when it is editable. Enter commits and moves down; Tab commits and moves right.
  • Cmd/Ctrl + C, X, VCopy, cut and paste the range as tab-separated text, compatible with Excel and Google Sheets.
  • Delete / BackspaceClear editable cells in the range.
  • Cmd/Ctrl + ASelect every visible cell.
  • Cmd/Ctrl + Z, Shift + Cmd/Ctrl + ZUndo and redo edits made in the grid.