Scroll Containers
The default table renders its own scrolling element. Three props change that — pick one when the table shares a viewport with surrounding page chrome, or when virtualization needs to be driven by a different scrolling element.
import * as React from 'react'
import { DataTable, DataTableCell, DataTableColumn, DataTableColumnHeader } from '@/components/ui/data-table'
import { localModel } from '@virtuoso.dev/data-table'
const rows = Array.from({ length: 80 }, (_, index) => ({
id: `SKU-${String(index + 1).padStart(3, '0')}`,
name: `Product ${index + 1}`,
category: ['Office', 'Peripherals', 'Audio'][index % 3]!,
}))
const model = localModel({ data: rows })
export default function App() {
const [scrollParent, setScrollParent] = React.useState<HTMLDivElement | null>(null)
return (
<div ref={setScrollParent} className="h-[360px] overflow-auto rounded-xl border">
<div className="sticky top-0 z-10 border-b bg-background px-3 py-2 text-sm font-medium">Inventory workspace</div>
<DataTable computeRowKey={({ data }) => data.id} customScrollParent={scrollParent} increaseViewportBy={360} model={model}>
<DataTableColumn field="name">
<DataTableColumnHeader>Product</DataTableColumnHeader>
<DataTableCell className="font-medium">{({ cellValue }) => String(cellValue)}</DataTableCell>
</DataTableColumn>
<DataTableColumn field="category">
<DataTableColumnHeader>Category</DataTableColumnHeader>
<DataTableCell>{({ cellValue }) => String(cellValue)}</DataTableCell>
</DataTableColumn>
</DataTable>
</div>
)
}Window scrolling
Section titled “Window scrolling”useWindowScroll makes the document viewport drive the table’s scroll state:
<DataTable model={model} useWindowScroll>
{/* columns */}
</DataTable>The table no longer renders an internal scroller; its height comes from rendered content and the surrounding page layout. Sticky headers now interact with document-level sticky offsets, so test them against your actual header/nav stack.
Existing parent element
Section titled “Existing parent element”customScrollParent attaches the table to an existing scrolling element:
const [scrollParent, setScrollParent] = React.useState<HTMLDivElement | null>(null)
return (
<div ref={setScrollParent} className="h-[480px] overflow-auto">
<Toolbar />
<DataTable customScrollParent={scrollParent} model={model}>
{/* columns */}
</DataTable>
</div>
)Custom scroll element
Section titled “Custom scroll element”ScrollElement replaces the default scroller component while keeping the rest of the table contained:
const ScrollElement = React.forwardRef<HTMLDivElement, React.HTMLProps<HTMLDivElement>>(function ScrollElement(props, ref) {
return <div ref={ref} className="custom-scrollbar overflow-auto" {...props} />
})
<DataTable ScrollElement={ScrollElement} model={model} />Pick one scroll mode per table. For customScrollParent, make sure that element has a real height and is the element that actually scrolls.