Column Resizing Example
A resize handle on every column plus an external button that sets the product column to a fixed width. Both publish to the same resizeColumn$ stream; columnWidths$ exposes the realized layout for the readout.
APIs used
Section titled “APIs used”HeaderEdge— slot position for the handleResizeHandle— shadcn drag affordanceresizeColumn$— width override streamcolumnWidths$— realized layout state for display
import { Button } from '@/components/ui/button'
import { DataTable, DataTableCell, DataTableColumn, DataTableColumnHeader, HeaderEdge } from '@/components/ui/data-table'
import { ResizeHandle } from '@/components/ui/data-table/column-resize'
import { localModel, columnWidths$, columns$, useEngineRef, useRemoteCellValue, useRemotePublisher } from '@virtuoso.dev/data-table'
import { resetColumnWidthOverrides$, resizeColumn$ } from '@virtuoso.dev/data-table/column-resize'
import { products } from './data'
import type { ColumnInfo } from '@virtuoso.dev/data-table'
function findColumnKey(columns: Map<string, ColumnInfo> | undefined, field: string) {
return columns ? [...columns].find(([, info]) => info.field === field)?.[0] : undefined
}
const model = localModel({ data: products })
export default function App() {
const engineRef = useEngineRef()
const columns = useRemoteCellValue(columns$, engineRef)
const widths = useRemoteCellValue(columnWidths$, engineRef)
const resizeColumn = useRemotePublisher(resizeColumn$, engineRef)
const resetColumnWidthOverrides = useRemotePublisher(resetColumnWidthOverrides$, engineRef)
const nameKey = findColumnKey(columns, 'name')
const nameWidth = nameKey && widths ? Math.round(widths.get(nameKey) ?? 0) : 0
return (
<div className="space-y-4">
<div className="flex flex-wrap items-center gap-2 text-sm">
<Button
disabled={!nameKey}
onClick={() => {
if (nameKey) {
resizeColumn({ key: nameKey, width: 280 })
}
}}
variant="outline"
>
Set product to 280px
</Button>
<Button onClick={() => resetColumnWidthOverrides()} variant="outline">
Reset widths
</Button>
<span className="text-muted-foreground">Product width: {nameWidth || 'measuring'}px</span>
</div>
<DataTable className="rounded-xl" model={model} engineRef={engineRef} style={{ height: 400 }}>
<DataTableColumn field="name">
<DataTableColumnHeader>
<HeaderEdge component={ResizeHandle} />
{() => 'Product'}
</DataTableColumnHeader>
<DataTableCell className="font-medium">{({ row }) => row.data.name}</DataTableCell>
</DataTableColumn>
<DataTableColumn field="category">
<DataTableColumnHeader>
<HeaderEdge component={ResizeHandle} />
{() => 'Category'}
</DataTableColumnHeader>
<DataTableCell>{({ cellValue }) => String(cellValue)}</DataTableCell>
</DataTableColumn>
<DataTableColumn field="stock">
<DataTableColumnHeader className="justify-end">
<HeaderEdge component={ResizeHandle} />
{() => 'Stock'}
</DataTableColumnHeader>
<DataTableCell className="text-right tabular-nums">{({ cellValue }) => String(cellValue)}</DataTableCell>
</DataTableColumn>
</DataTable>
</div>
)
}Drag handles cover spreadsheet-style interaction. External controls cover presets, reset buttons, and commands that shouldn’t depend on a pointer — both publish to the same stream, so they stay consistent.