NuGrid provides flexible row selection with support for single and multi-select modes, keyboard shortcuts, and programmatic control.
<script setup lang="ts">
import type { NuGridColumn } from '#nu-grid/types'
interface Employee {
id: number
name: string
department: string
role: string
}
const data = ref<Employee[]>([
{ id: 1, name: 'Alice Johnson', department: 'Engineering', role: 'Developer' },
{ id: 2, name: 'Bob Smith', department: 'Marketing', role: 'Manager' },
{ id: 3, name: 'Carol White', department: 'Engineering', role: 'Lead' },
{ id: 4, name: 'David Brown', department: 'HR', role: 'Specialist' },
{ id: 5, name: 'Emma Davis', department: 'Engineering', role: 'Developer' },
])
const columns: NuGridColumn<Employee>[] = [
{ accessorKey: 'id', header: 'ID', size: 60 },
{ accessorKey: 'name', header: 'Name', size: 150 },
{ accessorKey: 'department', header: 'Department', size: 120 },
{ accessorKey: 'role', header: 'Role', size: 120 },
]
const rowSelection = ref({})
const table = useTemplateRef('table')
const selectedCount = computed(() => {
return Object.values(rowSelection.value).filter(Boolean).length
})
</script>
<template>
<div class="w-full space-y-4">
<NuGrid
ref="table"
v-model:row-selection="rowSelection"
:data="data"
:columns="columns"
selection="multi"
:ui="{
base: 'w-full border-separate border-spacing-0',
thead: '[&>tr]:bg-elevated/50',
th: 'py-2 border-y border-default first:border-l last:border-r first:rounded-l-lg last:rounded-r-lg',
td: 'border-b border-default',
}"
/>
<div class="text-sm text-muted">{{ selectedCount }} row(s) selected</div>
</div>
</template>
Enable row selection with the selection prop:
<template>
<!-- Multi-select with checkboxes -->
<NuGrid :data="data" :columns="columns" selection="multi" />
<!-- Single-select (radio button style) -->
<NuGrid :data="data" :columns="columns" selection="single" />
</template>
Track selection state with v-model:
<script setup lang="ts">
const rowSelection = ref<Record<string, boolean>>({})
// Pre-select rows
const rowSelection = ref({
'0': true, // First row selected
'2': true, // Third row selected
})
</script>
<template>
<NuGrid
v-model:row-selection="rowSelection"
:data="data"
:columns="columns"
selection="multi"
/>
</template>
For more control, pass an options object:
<script setup lang="ts">
const selectionOptions = computed(() => ({
mode: 'multi', // 'single' or 'multi'
hidden: false, // Hide selection column
enabled: true, // Enable/disable selection
rowSelectionEnabled: (row) => row.original.status !== 'locked',
}))
</script>
<template>
<NuGrid
:data="data"
:columns="columns"
:selection="selectionOptions"
/>
</template>
| Option | Type | Description |
|---|---|---|
mode | 'single' | 'multi' | Selection mode |
hidden | boolean | Hide the selection checkbox column |
enabled | boolean | Enable/disable all selection |
rowSelectionEnabled | (row) => boolean | Per-row selection control |
Disable selection for specific rows:
<script setup lang="ts">
const selectionOptions = {
mode: 'multi',
rowSelectionEnabled: (row) => {
// Disable selection for inactive users
return row.original.status !== 'inactive'
},
}
</script>
<template>
<NuGrid
:data="data"
:columns="columns"
:selection="selectionOptions"
/>
</template>
Access selected rows via the grid ref:
<script setup lang="ts">
const gridRef = useTemplateRef('grid')
function processSelectedRows() {
const selected = gridRef.value?.getSelectedRows()
console.log('Selected:', selected)
}
function clearSelection() {
rowSelection.value = {}
}
</script>
<template>
<NuGrid
ref="grid"
v-model:row-selection="rowSelection"
:data="data"
:columns="columns"
selection="multi"
/>
<div class="mt-4 flex gap-2">
<UButton @click="processSelectedRows">Process Selected</UButton>
<UButton color="neutral" @click="clearSelection">Clear</UButton>
</div>
</template>
Listen to selection changes:
<script setup lang="ts">
function onRowSelectionChanged(selection: Record<string, boolean>) {
const selectedCount = Object.values(selection).filter(Boolean).length
console.log(`${selectedCount} rows selected`)
}
</script>
<template>
<NuGrid
:data="data"
:columns="columns"
selection="multi"
@row-selection-changed="onRowSelectionChanged"
/>
</template>
When selection is enabled:
| Key | Action |
|---|---|
Space | Toggle selection on focused row |
Shift + Click | Select range (multi-select) |
Ctrl/Cmd + Click | Toggle individual row (multi-select) |
Keep selection functionality without showing checkboxes:
<template>
<NuGrid
v-model:row-selection="rowSelection"
:data="data"
:columns="columns"
:selection="{ mode: 'multi', hidden: true }"
/>
</template>
With hidden selection, users can still select rows by clicking on them.
Combine selection with row focus mode for a master-detail pattern:
<script setup lang="ts">
const focusedRowId = ref<string | null>(null)
</script>
<template>
<div class="flex gap-4">
<NuGrid
v-model:focused-row-id="focusedRowId"
:data="data"
:columns="columns"
:focus="{ mode: 'row' }"
selection="single"
class="flex-1"
/>
<div v-if="focusedRowId" class="w-80">
<!-- Detail panel for focused row -->
</div>
</div>
</template>
Selected rows receive the data-selected="true" attribute. Style them with CSS:
<template>
<NuGrid
:data="data"
:columns="columns"
selection="multi"
:ui="{
tbody: '[&>tr[data-selected=true]]:bg-primary/10',
}"
/>
</template>
A common pattern with selection:
<script setup lang="ts">
const gridRef = useTemplateRef('grid')
const rowSelection = ref({})
const selectedCount = computed(() =>
Object.values(rowSelection.value).filter(Boolean).length
)
async function deleteSelected() {
const selected = gridRef.value?.getSelectedRows()
if (!selected?.length) return
// Confirm and delete
await deleteUsers(selected.map(u => u.id))
// Clear selection after delete
rowSelection.value = {}
}
</script>
<template>
<div>
<div class="mb-4 flex items-center gap-2">
<UButton
v-if="selectedCount > 0"
color="error"
icon="i-lucide-trash"
@click="deleteSelected"
>
Delete {{ selectedCount }} selected
</UButton>
</div>
<NuGrid
ref="grid"
v-model:row-selection="rowSelection"
:data="data"
:columns="columns"
selection="multi"
/>
</div>
</template>