Row virtualization renders only the visible window of rows, so the Data Grid stays fast with 100,000 rows and beyond — sorting, filtering and selection run across the full dataset.
Virtualization keeps the DOM small no matter how large the dataset is: only the
rows currently in view (plus a small buffer) are rendered as real elements.
Reach for it whenever you bind more rows than the browser can comfortably paint
at once — a few thousand and up. It is on by default (virtualization)
and is mutually exclusive with pagination.
100,000 rows, virtualized
Only the visible window of rows exists in the DOM — scroll, sort, filter and select across the full dataset. This live demo runs on 100,000 generated rows.
<script setup lang="ts">
import { CDataGrid } from '@coreui/vue-data-grid'
const firstNames = ['Alice', 'Bob', 'Carol', 'Dave', 'Eve', 'Frank', 'Grace', 'Heidi', 'Ivan', 'Judy']
const lastNames = ['Smith', 'Jones', 'Brown', 'Taylor', 'Wilson', 'Davies', 'Evans', 'Thomas']
const roles = ['admin', 'editor', 'viewer']
const statuses = ['active', 'pending', 'banned']
const items = Array.from({ length: 100_000 }, (_, i) => {
const name = `${firstNames[i % firstNames.length]} ${lastNames[i % lastNames.length]}`
return {
id: i + 1,
name,
email: `${name.toLowerCase().replace(' ', '.')}${i}@example.com`,
role: roles[i % roles.length],
status: statuses[i % statuses.length],
score: (i * 37) % 1000
}
})
const columns = [
{ key: 'id', label: '#', width: 90 },
{ key: 'name', label: 'Name', width: 200 },
{ key: 'email', label: 'Email', width: 260 },
{ key: 'role', label: 'Role', width: 110 },
{ key: 'status', label: 'Status', width: 110 },
{ key: 'score', label: 'Score', width: 90 }
]
</script>
<template>
<CDataGrid
:columns="columns"
:items="items"
:item-key="(item) => String(item.id)"
column-filters
global-filter
row-selection
/>
</template> How it works
The grid measures the scroll viewport and renders only the rows that intersect it. Two props tune the behavior:
rowHeight— the estimated row height in px (default44) the virtualizer uses to size the scroll area and decide how many rows fit.overscan— extra rows rendered above and below the visible window (default10) to smooth fast scrolling. Raise it if you see blank rows while flinging; lower it to shave DOM nodes.
Fixed row height is assumed in this release — dynamic per-row heights are on the roadmap. For datasets larger than browser memory, hand paging to your backend with server-side data.
See the Performance guide for tuning advice.