@toolbox-web/grid-vue
| Install | |
|---|---|
npm install @toolbox-web/grid-vue |
|
| Latest Version: | 2.6.3 |
| Vue: | 3 |
| License: | MIT |
| Last Updated: | Oct 23, 2026 |
| Links: | GitHub · npm |
@toolbox-web/grid-vue
Vue 3 adapter for @toolbox-web/grid data grid component. Provides components and composables for declarative Vue integration with custom cell renderers and editors.
Features
- ✅ Full Vue 3 integration - Use Vue components for cell renderers and editors
- ✅ Composition API - Built with
<script setup>and TypeScript generics - ✅ Declarative feature props - Enable plugins with simple props like
selection="range" - ✅ Tree-shakeable features - Only import the features you use
- ✅ Slot-based customization -
#celland#editorslots for custom rendering - ✅ Type-level defaults - App-wide renderers/editors via
GridTypeProvider - ✅ Icon configuration - App-wide icon overrides via
GridProviderorGridIconProvider - ✅ Composables -
useGridfor programmatic access - ✅ Master-detail -
TbwGridDetailPanelfor expandable rows - ✅ Tool panels -
TbwGridToolPanelfor custom sidebar content - ✅ Responsive cards -
TbwGridResponsiveCardfor mobile layouts - ✅ Full type safety - TypeScript generics support
- ✅ Vue 3.4+ - Supports
defineModeland improved generics
Installation
# npm
npm install @toolbox-web/grid @toolbox-web/grid-vue
# yarn
yarn add @toolbox-web/grid @toolbox-web/grid-vue
# pnpm
pnpm add @toolbox-web/grid @toolbox-web/grid-vue
# bun
bun add @toolbox-web/grid @toolbox-web/grid-vue
Quick Start
1. Register the Grid Component
In your application entry point, import the grid registration:
// main.ts
import '@toolbox-web/grid';
2. Use in Components
<script setup lang="ts">
import { TbwGrid, TbwGridColumn } from '@toolbox-web/grid-vue';
import { ref } from 'vue';
interface Employee {
id: number;
name: string;
department: string;
salary: number;
}
const employees = ref<Employee[]>([
{ id: 1, name: 'Alice', department: 'Engineering', salary: 95000 },
{ id: 2, name: 'Bob', department: 'Marketing', salary: 75000 },
{ id: 3, name: 'Charlie', department: 'Sales', salary: 85000 },
]);
</script>
<template>
<TbwGrid :rows="employees">
<TbwGridColumn field="id" header="ID" :width="60" />
<TbwGridColumn field="name" header="Name" :sortable="true" />
<TbwGridColumn field="department" header="Department" :sortable="true" />
<TbwGridColumn field="salary" header="Salary" type="number" />
</TbwGrid>
</template>
TbwGrid Props
| Prop | Type | Description |
|---|---|---|
rows |
TRow[] |
Row data to display |
columns |
ColumnConfig[] |
Column definitions |
gridConfig |
GridConfig |
Full configuration object |
fitMode |
'stretch' | 'fixed' |
Column sizing mode (default: 'stretch') |
sortable |
boolean |
Grid-wide sorting toggle (default: true) |
filterable |
boolean |
Grid-wide filtering toggle (default: true) |
selectable |
boolean |
Grid-wide selection toggle (default: true) |
loading |
boolean |
Show loading overlay (default: false) |
customStyles |
string |
CSS injected via document.adoptedStyleSheets |
Enabling Features
Features are enabled using declarative props with side-effect imports. This gives you the best of both worlds: clean, intuitive templates and tree-shakeable bundles.
How It Works
- Import the feature - A side-effect import registers the feature factory
- Use the prop - TbwGrid detects the prop and creates the plugin instance
<script setup lang="ts">
import { TbwGrid } from '@toolbox-web/grid-vue';
// 1. Import features you need (once, typically in main.ts or the component file)
import '@toolbox-web/grid-vue/features/selection';
import '@toolbox-web/grid-vue/features/multi-sort';
import '@toolbox-web/grid-vue/features/filtering';
</script>
<template>
<!-- 2. Use declarative props - no manual plugin instantiation! -->
<TbwGrid :rows="employees" :columns="columns" selection="range" multi-sort filtering />
</template>
Why Side-Effect Imports?
- Tree-shakeable - Only the features you import are bundled
- Synchronous - No loading states, no HTTP requests, no spinners
- Type-safe - Full TypeScript support for feature props
- Clean templates - No
plugins: [new SelectionPlugin({ mode: 'range' })]boilerplate
Available Features
Import from @toolbox-web/grid-vue/features/<name>:
| Feature | Prop | Example |
|---|---|---|
selection |
selection |
selection="range" or :selection="{ mode: 'row', checkbox: true }" |
multi-sort |
multi-sort |
multi-sort or :multi-sort="{ maxSortLevels: 3 }" |
filtering |
filtering |
filtering or :filtering="{ debounceMs: 200 }" |
editing |
editing |
editing="dblclick" or editing="click" |
clipboard |
clipboard |
clipboard (requires selection) |
undoRedo |
undoRedo |
undoRedo (requires editing — emits separate @undo/@redo events) |
context-menu |
context-menu |
context-menu |
reorder |
reorder |
reorder (column drag-to-reorder) |
row-reorder |
row-reorder |
row-reorder (row drag-to-reorder) |
visibility |
visibility |
visibility (column visibility panel) |
pinned-columns |
pinned-columns |
pinned-columns |
pinned-rows |
pinned-rows |
pinned-rows |
grouping-columns |
grouping-columns |
grouping-columns |
grouping-rows |
grouping-rows |
:grouping-rows="{ groupBy: 'department' }" |
tree |
tree |
:tree="{ childrenField: 'children' }" |
column-virtualization |
column-virtualization |
column-virtualization |
export |
export |
export |
print |
print |
print |
responsive |
responsive |
responsive (card layout on mobile) |
master-detail |
master-detail |
master-detail (use with <TbwGridDetailPanel>) |
pivot |
pivot |
:pivot="{ rowFields: [...], columnFields: [...] }" |
server-side |
server-side |
:server-side="{ ... }" |
Import All Features
For prototyping or when bundle size isn't critical, import all features at once:
// Import all features (larger bundle)
import '@toolbox-web/grid-vue/features';
Custom Cell Renderers
Use the #cell slot on TbwGridColumn for custom rendering. Type the column
with your row shape via the generic <TbwGridColumn<Employee>> syntax — slot
props (row, value) then narrow automatically with no per-template
annotation needed:
<script setup lang="ts">
import { TbwGrid, TbwGridColumn } from '@toolbox-web/grid-vue';
import StatusBadge from './StatusBadge.vue';
interface Employee {
id: string;
name: string;
status: 'active' | 'inactive';
}
</script>
<template>
<TbwGrid<Employee> :rows="employees">
<TbwGridColumn<Employee> field="name" header="Name" />
<TbwGridColumn<Employee> field="status" header="Status">
<template #cell="{ value, row }">
<!-- row: Employee, value: any (per-column) -->
<StatusBadge :status="value" :employee="row" />
</template>
</TbwGridColumn>
</TbwGrid>
</template>
If you also know the value type, pass both generics:
<TbwGridColumn<Employee, string>> (or annotate the slot prop with
CellSlotProps<Employee, string>).
Slot Props
The #cell slot receives:
| Prop | Type | Description |
|---|---|---|
value |
TValue |
Cell value |
row |
TRow |
Row data |
column |
object |
Column configuration |
rowIndex |
number |
Row index |
colIndex |
number |
Column index |
Generic defaults:
CellSlotProps<TRow = unknown, TValue = any>andTbwGridColumn<TRow = unknown, TValue = any>. SpecifyTRowon the column (e.g.<TbwGridColumn<Employee>>) and slot props inherit it automatically;TValuedefaults toanybecause the value shape is per-column and awkward to narrow inside template expressions.
Custom Cell Editors
Use the #editor slot for inline editing. Same generic-typing pattern as
#cell:
<script setup lang="ts">
import { TbwGrid, TbwGridColumn } from '@toolbox-web/grid-vue';
import '@toolbox-web/grid-vue/features/editing';
</script>
<template>
<TbwGrid<Employee> :rows="employees" editing="dblclick">
<TbwGridColumn<Employee> field="status" header="Status" :editable="true">
<template #editor="{ value, commit, cancel }">
<!-- value: any, commit: (newValue: any) => void -->
<select :value="value" @change="(e) => commit((e.target as HTMLSelectElement).value)">
<option value="active">Active</option>
<option value="inactive">Inactive</option>
</select>
</template>
</TbwGridColumn>
</TbwGrid>
</template>
Editor Slot Props
| Prop | Type | Description |
|---|---|---|
value |
TValue |
Current cell value |
row |
TRow |
Row data |
commit |
(value: any) => void |
Save the new value |
cancel |
() => void |
Cancel editing |
Generic defaults:
EditorSlotProps<TRow = unknown, TValue = any>— same rationale asCellSlotPropsabove. SpecifyTRowfor row-shape safety; passTValuewhen you know the column's value type.
Events
Handle grid events using Vue's template syntax.
Template Event Binding
<script setup lang="ts">
import { TbwGrid } from '@toolbox-web/grid-vue';
import type { CellClickDetail, SortChangeDetail } from '@toolbox-web/grid';
function onCellClick(e: CustomEvent<CellClickDetail>) {
console.log(`Clicked ${e.detail.field} at row ${e.detail.rowIndex}`);
}
function onSortChange(e: CustomEvent<SortChangeDetail>) {
console.log(`Sorted by ${e.detail.field} ${e.detail.direction}`);
}
</script>
<template>
<TbwGrid :rows="rows" @cell-click="onCellClick" @sort-change="onSortChange">
<TbwGridColumn field="name" header="Name" sortable />
</TbwGrid>
</template>
Available Events
| Event | Detail Type | Description |
|---|---|---|
@cell-click |
CellClickDetail |
Cell was clicked |
@row-click |
RowClickDetail |
Row was clicked |
@cell-activate |
CellActivateDetail |
Cell activated (cancelable) |
@cell-change |
CellChangeDetail |
Row updated via API |
@cell-commit |
CellCommitDetail |
Cell value committed (cancelable) |
@row-commit |
RowCommitDetail |
Row edit completed (cancelable) |
@changed-rows-reset |
ChangedRowsResetDetail |
Changed rows state was reset |
@sort-change |
SortChangeDetail |
Sort state changed |
@filter-change |
FilterChangeDetail |
Filter state changed |
@column-resize |
ColumnResizeDetail |
Column was resized |
@column-move |
ColumnMoveDetail |
Column was reordered |
@column-visibility |
ColumnVisibilityDetail |
Column visibility toggled |
@column-state-change |
GridColumnState |
Column visibility/order changed |
@selection-change |
SelectionChangeDetail |
Selection state changed |
@row-move |
RowMoveDetail |
Row was reordered |
@group-toggle |
GroupToggleDetail |
Row group expanded/collapsed |
@group-load-error |
GroupLoadErrorDetail |
Server group rows failed to load |
@tree-expand |
TreeExpandDetail |
Tree node expanded/collapsed |
@detail-expand |
DetailExpandDetail |
Master-detail row toggled |
@detail-load-error |
DetailLoadErrorDetail |
Server detail data failed to load |
@responsive-change |
ResponsiveChangeDetail |
Responsive layout mode changed |
@copy |
CopyDetail |
Data copied to clipboard |
@paste |
PasteDetail |
Data pasted from clipboard |
@undo |
UndoRedoDetail |
Undo action performed |
@redo |
UndoRedoDetail |
Redo action performed |
@export-complete |
ExportCompleteDetail |
Export operation completed |
@print-start |
PrintStartDetail |
Print operation started |
@print-complete |
PrintCompleteDetail |
Print operation completed |
Using Plugins (Advanced)
For full control, pass plugin instances directly via :grid-config. Use markRaw() to prevent Vue from making plugin instances reactive:
<script setup lang="ts">
import { markRaw } from 'vue';
import { TbwGrid, TbwGridColumn } from '@toolbox-web/grid-vue';
import { SelectionPlugin } from '@toolbox-web/grid/plugins/selection';
import { FilteringPlugin } from '@toolbox-web/grid/plugins/filtering';
const gridConfig = {
plugins: markRaw([new SelectionPlugin({ mode: 'range' }), new FilteringPlugin({ debounceMs: 200 })]),
};
</script>
<template>
<TbwGrid :rows="rows" :grid-config="gridConfig">
<TbwGridColumn field="name" header="Name" />
</TbwGrid>
</template>
Important: Always wrap plugin arrays with
markRaw(). Vue's reactivity proxy breaks plugin internal state.
You can also pass plugins through the dedicated :plugins prop, matching
<DataGrid plugins={...}> in React and [plugins] in Angular. When :plugins is set,
feature props are ignored — the array is the full plugin list:
<script setup lang="ts">
import { markRaw } from 'vue';
import { SelectionPlugin } from '@toolbox-web/grid/plugins/selection';
const plugins = markRaw([new SelectionPlugin({ mode: 'range' })]);
</script>
<template>
<TbwGrid :rows="rows" :plugins="plugins" />
</template>
Composables
useGrid
Access grid methods and state programmatically:
<script setup lang="ts">
import { useGrid } from '@toolbox-web/grid-vue';
const {
isReady,
config,
forceLayout,
getConfig,
ready,
getPlugin,
toggleGroup,
registerStyles,
unregisterStyles,
getVisibleColumns,
} = useGrid();
</script>
| Property / Method | Type | Description |
|---|---|---|
isReady |
Ref<boolean> |
Reactive flag — true once grid is ready |
config |
Ref<GridConfig | null> |
Reactive effective grid configuration |
element |
Ref<DataGridElement | null> |
Raw grid element reference |
gridElement |
Ref<DataGridElement | null> |
Deprecated alias for element |
ready() |
() => Promise<void> |
Wait for grid to finish initializing |
forceLayout() |
() => Promise<void> |
Force layout recalculation |
getConfig() |
() => Promise<GridConfig | null> |
Get effective configuration snapshot |
getPlugin(pluginClass) |
<T>(cls: new (...) => T) => T | undefined |
Get plugin by class |
getPluginByName(name) |
(name: string) => Plugin | undefined |
Get plugin by name |
toggleGroup(key) |
(key: string) => Promise<void> |
Toggle group expansion |
registerStyles(id, css) |
(id: string, css: string) => void |
Register custom stylesheet |
unregisterStyles(id) |
(id: string) => void |
Remove custom stylesheet |
getVisibleColumns() |
() => ColumnConfig[] |
Get non-hidden columns |
Providers
GridTypeProvider
Define app-wide type renderers:
<script setup lang="ts">
import { GridTypeProvider, type TypeDefaultsMap } from '@toolbox-web/grid-vue';
import { h } from 'vue';
import CurrencyCell from './CurrencyCell.vue';
const typeDefaults: TypeDefaultsMap = {
currency: {
renderer: (ctx) => h(CurrencyCell, { value: ctx.value }),
},
percentage: {
renderer: (ctx) => h('span', `${(ctx.value * 100).toFixed(1)}%`),
},
};
</script>
<template>
<GridTypeProvider :defaults="typeDefaults">
<App />
</GridTypeProvider>
</template>
Then use in columns:
<TbwGridColumn field="salary" header="Salary" type="currency" />
GridIconProvider
Override grid icons app-wide:
<script setup lang="ts">
import { GridIconProvider } from '@toolbox-web/grid-vue';
const icons = {
sortAsc: '↑',
sortDesc: '↓',
expand: '+',
collapse: '−',
filter: '🔍',
};
</script>
<template>
<GridIconProvider :icons="icons">
<App />
</GridIconProvider>
</template>
GridProvider
Combined provider for both types and icons:
<script setup lang="ts">
import { GridProvider } from '@toolbox-web/grid-vue';
</script>
<template>
<GridProvider :type-defaults="typeDefaults" :icons="icons">
<App />
</GridProvider>
</template>
Master-Detail
Expandable row details:
<script setup lang="ts">
import { TbwGrid, TbwGridDetailPanel, type DetailPanelContext } from '@toolbox-web/grid-vue';
import '@toolbox-web/grid-vue/features/master-detail';
</script>
<template>
<TbwGrid :rows="employees" master-detail>
<TbwGridColumn field="name" header="Name" />
<TbwGridColumn field="department" header="Department" />
<TbwGridDetailPanel v-slot="{ row }: DetailPanelContext<Employee>">
<div class="detail-content">
<h4>{{ row.name }}</h4>
<p>Department: {{ row.department }}</p>
<p>Email: {{ row.email }}</p>
</div>
</TbwGridDetailPanel>
</TbwGrid>
</template>
Tool Panels
Custom sidebar panels:
<script setup lang="ts">
import { TbwGrid, TbwGridToolPanel, type ToolPanelContext } from '@toolbox-web/grid-vue';
</script>
<template>
<TbwGrid :rows="employees">
<TbwGridColumn field="name" header="Name" />
<TbwGridToolPanel id="stats" title="Statistics" v-slot="{ rows }: ToolPanelContext<Employee>">
<div class="stats-panel">
<p>Total: {{ rows.length }}</p>
<p>Avg Salary: {{ averageSalary(rows) }}</p>
</div>
</TbwGridToolPanel>
</TbwGrid>
</template>
Responsive Cards
Mobile-friendly card layout:
<script setup lang="ts">
import { TbwGrid, TbwGridResponsiveCard, type ResponsiveCardContext } from '@toolbox-web/grid-vue';
import '@toolbox-web/grid-vue/features/responsive';
</script>
<template>
<TbwGrid :rows="employees" responsive>
<TbwGridColumn field="name" header="Name" />
<TbwGridColumn field="department" header="Department" />
<TbwGridResponsiveCard v-slot="{ row }: ResponsiveCardContext<Employee>">
<div class="employee-card">
<h3>{{ row.name }}</h3>
<p>{{ row.department }}</p>
</div>
</TbwGridResponsiveCard>
</TbwGrid>
</template>
TypeScript Support
Full generic support for row types:
<script setup lang="ts" generic="T extends Employee">
import { TbwGrid, TbwGridColumn } from '@toolbox-web/grid-vue';
interface Employee {
id: number;
name: string;
salary: number;
}
const props = defineProps<{
employees: T[];
}>();
</script>
<template>
<TbwGrid :rows="props.employees">
<TbwGridColumn field="name" header="Name" />
<TbwGridColumn field="salary" header="Salary">
<template #cell="{ value }"> ${{ value.toLocaleString() }} </template>
</TbwGridColumn>
</TbwGrid>
</template>
API Reference
Components
| Component | Description |
|---|---|
TbwGrid |
Main grid wrapper component |
TbwGridColumn |
Declarative column definition |
TbwGridDetailPanel |
Master-detail row content |
TbwGridToolPanel |
Custom sidebar panel |
TbwGridHeaderContent |
Inject Vue content into the shell header zone |
TbwGridToolbarContent |
Inject Vue content into the shell toolbar zone |
TbwGridToolButtons |
Toolbar button container |
TbwGridResponsiveCard |
Mobile card layout template |
GridTypeProvider |
App-wide type defaults |
GridIconProvider |
App-wide icon overrides |
GridProvider |
Combined type + icon provider |
Composables
| Composable | Description |
|---|---|
useGrid |
Access grid methods |
Types
| Type | Description |
|---|---|
CellSlotProps |
Props for #cell slot |
EditorSlotProps |
Props for #editor slot |
DetailPanelContext |
Props for detail panel slot |
ToolPanelContext |
Props for tool panel slot |
ResponsiveCardContext |
Props for responsive card slot |
GridConfig |
Grid configuration type |
ColumnConfig |
Column configuration type |
CellRenderer |
Cell renderer type |
CellEditor |
Cell editor type |
TypeDefault |
Type default configuration |
TypeDefaultsMap |
Type defaults registry type |
Requirements
- Vue 3.4+ (uses
defineModeland improved generics) - @toolbox-web/grid (peer dependency)
Demo
See the Vue demo app for a full-featured example using @toolbox-web/grid-vue.
bun nx serve demo-vue
Building
bun nx build grid-vue
Running Tests
bun nx test grid-vue
License
MIT © Øystein Amundsen
Related Packages
wrap a vue component as a web component, for IE 11 too.
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 2.6.3 | >=3.3.0 | MIT | |
| 2.6.2 | >=3.3.0 | MIT | |
| 2.6.1 | >=3.3.0 | MIT | |
| 2.6.0 | >=3.3.0 | MIT | |
| 2.5.0 | >=3.3.0 | MIT | |
| 2.4.0 | >=3.3.0 | MIT | |
| 2.3.0 | >=3.3.0 | MIT | |
| 2.2.0 | >=3.3.0 | MIT | |
| 2.1.0 | >=3.3.0 | MIT | |
| 2.0.0 | >=3.3.0 | MIT | |
| 1.11.0 | >=3.3.0 | MIT | |
| 1.10.0 | >=3.3.0 | MIT | |
| 1.9.1 | >=3.3.0 | MIT | |
| 1.9.0 | >=3.3.0 | MIT | |
| 1.8.0 | >=3.3.0 | MIT | |
| 1.7.1 | >=3.3.0 | MIT | |
| 1.7.0 | >=3.3.0 | MIT | |
| 1.6.1 | >=3.3.0 | MIT | |
| 1.6.0 | >=3.3.0 | MIT | |
| 1.5.1 | >=3.3.0 | MIT | |
| 1.5.0 | >=3.3.0 | MIT | |
| 1.4.0 | >=3.3.0 | MIT | |
| 1.3.0 | >=3.3.0 | MIT | |
| 1.2.0 | >=3.3.0 | MIT | |
| 1.1.0 | >=3.3.0 | MIT | |
| 1.0.0 | >=3.3.0 | MIT | |
| 0.11.5 | >=3.3.0 | MIT | |
| 0.11.4 | >=3.3.0 | MIT | |
| 0.11.3 | >=3.3.0 | MIT | |
| 0.11.2 | >=3.3.0 | MIT |
Showing the latest 30 of 52. See every release on npm
Pre-releases (7)
| Version | Released | Vue | License |
|---|---|---|---|
| 2.0.0-rc.1 | >=3.3.0 | MIT | |
| 2.0.0-rc.0 | >=3.3.0 | MIT | |
| 2.0.0-beta.1 | >=3.3.0 | MIT | |
| 2.0.0-beta | >=3.3.0 | MIT | |
| 1.0.0-rc.2 | >=3.3.0 | MIT | |
| 1.0.0-rc.1 | >=3.3.0 | MIT | |
| 1.0.0-alpha | >=3.3.0 | MIT |