Inspecting Row Re-renders
unstableRowRender$ emits an event every time a row renders, with the row index, section, sticky flag, and group flag. Use it to answer “which rows rendered during this scroll or update?” — disabled by default; opt in with unstableEnableRowRenderEvents$.
import * as React from 'react'
import { Button } from '@/components/ui/button'
import { DataTable, DataTableCell, DataTableColumn, DataTableColumnHeader } from '@/components/ui/data-table'
import {
localModel,
scrollToRow$,
unstableEnableRowRenderEvents$,
unstableRowRender$,
useEngineRef,
usePublisher,
useRemotePublisher,
} from '@virtuoso.dev/data-table'
import { useEngine } from '@virtuoso.dev/reactive-engine-react'
import type { UnstableRowRenderEvent } from '@virtuoso.dev/data-table'
const rowRenderEvents = new EventTarget()
const rows = Array.from({ length: 80 }, (_, index) => ({
id: `SKU-${String(index + 1).padStart(3, '0')}`,
name: `Product ${index + 1}`,
status: ['In stock', 'Low stock', 'Backorder'][index % 3]!,
}))
function emitRowRender(event: UnstableRowRenderEvent) {
queueMicrotask(() => {
rowRenderEvents.dispatchEvent(new CustomEvent<UnstableRowRenderEvent>('row-render', { detail: event }))
})
}
function RowRenderProbe() {
const engine = useEngine()
const enableRowRenderEvents = usePublisher(unstableEnableRowRenderEvents$)
React.useLayoutEffect(() => {
enableRowRenderEvents(true)
const unsubscribe = engine.sub(unstableRowRender$, emitRowRender)
return () => {
unsubscribe()
enableRowRenderEvents(false)
}
}, [enableRowRenderEvents, engine])
return null
}
function EventLog() {
const [events, setEvents] = React.useState<UnstableRowRenderEvent[]>([])
React.useEffect(() => {
const recordEvent = (event: Event) => {
setEvents((current) => [(event as CustomEvent<UnstableRowRenderEvent>).detail, ...current].slice(0, 4))
}
rowRenderEvents.addEventListener('row-render', recordEvent)
return () => {
rowRenderEvents.removeEventListener('row-render', recordEvent)
}
}, [])
return (
<div className="rounded-lg border bg-muted/30 p-3 text-xs">
<div className="mb-1 text-sm text-muted-foreground">Captured events: {events.length}</div>
{events.length === 0 ? (
<span className="text-muted-foreground">No events captured</span>
) : (
events.map((event, index) => (
<div key={`${event.index}-${event.section}-${index}`} className="font-mono">
row {event.index}, {event.section}
</div>
))
)}
</div>
)
}
const model = localModel({ data: rows })
export default function App() {
const engineRef = useEngineRef()
const scrollToRow = useRemotePublisher(scrollToRow$, engineRef)
return (
<div className="space-y-3">
<div className="flex flex-wrap items-center gap-2">
<Button onClick={() => scrollToRow({ index: 0, behavior: 'smooth' })} variant="outline">
Top
</Button>
<Button onClick={() => scrollToRow({ index: 48, align: 'center', behavior: 'smooth' })} variant="outline">
Row 49
</Button>
</div>
<DataTable className="rounded-xl" computeRowKey={({ data }) => data.id} model={model} engineRef={engineRef} style={{ height: 320 }}>
<RowRenderProbe />
<DataTableColumn field="name">
<DataTableColumnHeader>Product</DataTableColumnHeader>
<DataTableCell className="font-medium">{({ cellValue }) => String(cellValue)}</DataTableCell>
</DataTableColumn>
<DataTableColumn field="status">
<DataTableColumnHeader>Status</DataTableColumnHeader>
<DataTableCell>{({ cellValue }) => String(cellValue)}</DataTableCell>
</DataTableColumn>
</DataTable>
<EventLog />
</div>
)
}import * as React from 'react'
import { unstableEnableRowRenderEvents$, unstableRowRender$, usePublisher } from '@virtuoso.dev/data-table'
import { useEngine } from '@virtuoso.dev/reactive-engine-react'
function RowRenderProbe() {
const engine = useEngine()
const enableRowRenderEvents = usePublisher(unstableEnableRowRenderEvents$)
React.useLayoutEffect(() => {
enableRowRenderEvents(true)
const unsubscribe = engine.sub(unstableRowRender$, (event) => {
console.log(event.index, event.section, event.sticky, event.group)
})
return () => {
unsubscribe()
enableRowRenderEvents(false)
}
}, [enableRowRenderEvents, engine])
return null
}Mount the probe as a child of the table so it can access the engine:
<DataTable model={model}>
<RowRenderProbe />
{/* columns */}
</DataTable>The unstable prefix is literal — event timing and payload details may change between versions. Use this for diagnostics, profiling, and tests, never as product behavior.