Columns are the foundation of NuGrid. They define how your data is displayed, edited, and interacted with.
<script setup lang="ts">
import type { NuGridColumn } from '#nu-grid/types'
interface Product {
id: number
name: string
price: number
category: string
inStock: boolean
}
const data = ref<Product[]>([
{ id: 1, name: 'Laptop Pro', price: 1299.99, category: 'Electronics', inStock: true },
{ id: 2, name: 'Wireless Mouse', price: 29.99, category: 'Accessories', inStock: true },
{ id: 3, name: 'USB-C Cable', price: 12.99, category: 'Accessories', inStock: false },
{ id: 4, name: 'Monitor 4K', price: 599.99, category: 'Electronics', inStock: true },
{ id: 5, name: 'Keyboard', price: 149.99, category: 'Accessories', inStock: true },
])
const columns: NuGridColumn<Product>[] = [
{
accessorKey: 'id',
header: 'ID',
size: 60,
enableEditing: false,
},
{
accessorKey: 'name',
header: 'Product Name',
size: 150,
},
{
accessorKey: 'price',
header: 'Price',
size: 100,
cell: ({ row }) => `$${row.original.price.toFixed(2)}`,
},
{
accessorKey: 'category',
header: 'Category',
size: 120,
},
{
accessorKey: 'inStock',
header: 'In Stock',
size: 100,
cell: ({ row }) =>
h(
'span',
{
class: row.original.inStock ? 'text-success' : 'text-error',
},
row.original.inStock ? 'Yes' : 'No',
),
},
]
</script>
<template>
<div class="w-full">
<NuGrid
:data="data"
:columns="columns"
resize-columns
: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>
</template>
At minimum, a column needs an accessorKey that matches a property in your data:
import type { NuGridColumn } from '#nu-grid/types'
interface User {
id: number
name: string
email: string
}
const columns: NuGridColumn<User>[] = [
{ accessorKey: 'id', header: 'ID' },
{ accessorKey: 'name', header: 'Name' },
{ accessorKey: 'email', header: 'Email' },
]
Control column width with sizing properties:
const columns: NuGridColumn<User>[] = [
{
accessorKey: 'id',
header: 'ID',
size: 60, // Default width in pixels
minSize: 40, // Minimum width when resizing
maxSize: 100, // Maximum width when resizing
},
{
accessorKey: 'name',
header: 'Name',
size: 200,
minSize: 100,
},
]
Enable or disable features per column:
const columns: NuGridColumn<User>[] = [
{
accessorKey: 'id',
header: 'ID',
enableSorting: false, // Disable sorting for this column
enableEditing: false, // Disable editing for this column
enableResizing: false, // Disable resizing for this column
enableHiding: false, // Prevent column from being hidden
enablePinning: true, // Allow column pinning
},
]
Customize how cell content is displayed:
const columns: NuGridColumn<User>[] = [
{
accessorKey: 'name',
header: 'Name',
cell: ({ row }) => {
return h('div', { class: 'flex items-center gap-2' }, [
h('img', { src: row.original.avatar, class: 'w-6 h-6 rounded-full' }),
h('span', row.original.name),
])
},
},
{
accessorKey: 'status',
header: 'Status',
cell: ({ row }) => {
const colors = {
active: 'text-success',
inactive: 'text-error',
}
return h('span', { class: colors[row.original.status] }, row.original.status)
},
},
]
Customize the column header:
const columns: NuGridColumn<User>[] = [
{
accessorKey: 'email',
header: ({ column }) => {
const isSorted = column.getIsSorted()
return h(UButton, {
color: 'neutral',
variant: 'ghost',
label: 'Email',
icon: isSorted === 'asc' ? 'i-lucide-arrow-up' : 'i-lucide-arrow-down',
onClick: () => column.toggleSorting(),
})
},
},
]
NuGrid includes built-in cell types for common data formats. These provide appropriate formatting and editing:
const columns: NuGridColumn<Product>[] = [
{ accessorKey: 'name', header: 'Name', cellDataType: 'text' },
{ accessorKey: 'price', header: 'Price', cellDataType: 'currency' },
{ accessorKey: 'quantity', header: 'Qty', cellDataType: 'number' },
{ accessorKey: 'active', header: 'Active', cellDataType: 'boolean' },
{ accessorKey: 'createdAt', header: 'Created', cellDataType: 'date' },
{ accessorKey: 'rating', header: 'Rating', cellDataType: 'rating' },
]
| Type | Description |
|---|---|
text | Plain text with text input editor |
number | Numeric values with number input |
currency | Currency formatted values |
boolean | Checkbox display and editor |
date | Date values with date picker |
rating | Star rating display |
selection | Dropdown selection |
lookup | Lookup from options |
action-menu | Action menu column |
Group related columns under a common header:
import { createColumnHelper } from '#nu-grid'
const columnHelper = createColumnHelper<User>()
const columns = [
columnHelper.group({
id: 'personal',
header: 'Personal Info',
columns: [
{ accessorKey: 'name', header: 'Name' },
{ accessorKey: 'email', header: 'Email' },
],
}),
columnHelper.group({
id: 'work',
header: 'Work Info',
columns: [
{ accessorKey: 'department', header: 'Department' },
{ accessorKey: 'role', header: 'Role' },
],
}),
]
Pin columns to the left or right edge:
<script setup lang="ts">
const columnPinning = ref({
left: ['id', '__selection'], // Pin selection and ID to left
right: ['__actions'], // Pin actions to right
})
</script>
<template>
<NuGrid
v-model:column-pinning="columnPinning"
:data="data"
:columns="columns"
/>
</template>
Control which columns are visible:
<script setup lang="ts">
const columnVisibility = ref({
email: false, // Hide email column
phone: false, // Hide phone column
})
</script>
<template>
<NuGrid
v-model:column-visibility="columnVisibility"
:data="data"
:columns="columns"
/>
</template>
Enable column resizing:
<template>
<NuGrid
:data="data"
:columns="columns"
resize-columns
:column-sizing-options="{
columnResizeMode: 'onChange',
}"
/>
</template>
Configure how columns behave in the add-new-row:
const columns: NuGridColumn<Product>[] = [
{
accessorKey: 'id',
header: 'ID',
showNew: false, // Hide in add row (auto-generated)
},
{
accessorKey: 'name',
header: 'Name',
requiredNew: true, // Required in add row
},
{
accessorKey: 'price',
header: 'Price',
defaultValue: 0, // Default value in add row
validateNew: (value) => {
if (value < 0) {
return { valid: false, message: 'Price must be positive' }
}
return { valid: true }
},
},
]