Layout & Display

Virtualization

Handle large datasets efficiently with virtualization.

NuGrid uses virtualization to efficiently render large datasets by only rendering the visible rows.

Rendering 1,000 rows with virtualization. Only visible rows are rendered in the DOM.

<script setup lang="ts">
import type { NuGridColumn } from '#nu-grid/types'

interface Row {
  id: number
  name: string
  value: number
  category: string
}

// Generate 1000 rows
const data = ref<Row[]>(
  Array.from({ length: 1000 }, (_, i) => ({
    id: i + 1,
    name: `Item ${i + 1}`,
    value: Math.floor(Math.random() * 10000),
    category: ['A', 'B', 'C', 'D'][i % 4]!,
  })),
)

const columns: NuGridColumn<Row>[] = [
  { accessorKey: 'id', header: 'ID', size: 80 },
  { accessorKey: 'name', header: 'Name', size: 150 },
  { accessorKey: 'value', header: 'Value', size: 100 },
  { accessorKey: 'category', header: 'Category', size: 100 },
]
</script>

<template>
  <div class="w-full">
    <p class="mb-3 text-sm text-muted">
      Rendering 1,000 rows with virtualization. Only visible rows are rendered in the DOM.
    </p>
    <div class="h-80">
      <NuGrid
        :data="data"
        :columns="columns"
        virtualization
        :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>
  </div>
</template>

Enabling Virtualization

Enable virtualization with the virtualization prop:

<template>
  <NuGrid
    :data="data"
    :columns="columns"
    virtualization
  />
</template>

When to Use Virtualization

Dataset SizeRecommendation
< 100 rowsNot needed
100-500 rowsOptional
500-1000 rowsRecommended
> 1000 rowsStrongly recommended

How It Works

Virtualization renders only the rows visible in the viewport plus a small buffer. As the user scrolls:

  1. New rows entering the viewport are rendered
  2. Rows leaving the viewport are removed from the DOM
  3. The scroll position is maintained using spacer elements

This keeps the DOM size constant regardless of data size.

Container Height

For virtualization to work properly, the grid needs a defined height:

<template>
  <!-- Fixed height -->
  <div class="h-[600px]">
    <NuGrid :data="data" :columns="columns" virtualization />
  </div>

  <!-- Flex container -->
  <div class="flex h-screen flex-col">
    <header>Header</header>
    <div class="flex-1 min-h-0">
      <NuGrid :data="data" :columns="columns" virtualization />
    </div>
  </div>
</template>

Row Height

NuGrid calculates row heights automatically, but you can optimize by providing estimates:

<template>
  <NuGrid
    :data="data"
    :columns="columns"
    virtualization
    :estimated-row-height="40"
  />
</template>

Variable Row Heights

Virtualization handles variable row heights automatically:

const columns: NuGridColumn<Item>[] = [
  {
    accessorKey: 'description',
    header: 'Description',
    cell: ({ row }) => {
      // Multi-line content is handled correctly
      return h('div', { class: 'whitespace-pre-wrap' }, row.original.description)
    },
  },
]

Scroll Restoration

NuGrid maintains scroll position during:

  • Data updates
  • Column changes
  • Window resizing

Performance Tips

1. Minimize Cell Complexity

Keep cell renderers simple:

// Good - simple render
{
  cell: ({ row }) => row.original.name
}

// Avoid - complex computation in render
{
  cell: ({ row }) => {
    const result = expensiveComputation(row.original)
    return formatResult(result)
  }
}

2. Use Computed Properties

Move calculations outside the render:

<script setup lang="ts">
const processedData = computed(() =>
  data.value.map(item => ({
    ...item,
    computedField: expensiveComputation(item),
  }))
)
</script>

<template>
  <NuGrid
    :data="processedData"
    :columns="columns"
    virtualization
  />
</template>

3. Avoid Reactive Dependencies in Cells

// Avoid - creates many reactive dependencies
{
  cell: ({ row }) => someRef.value + row.original.value
}

// Better - use column-level computed
{
  cell: ({ row, table }) => {
    const context = table.options.meta
    return context.multiplier * row.original.value
  }
}

4. Use Column Visibility

Hide columns that aren't needed:

<script setup lang="ts">
const columnVisibility = ref({
  internalId: false,
  debugInfo: false,
})
</script>

<template>
  <NuGrid
    v-model:column-visibility="columnVisibility"
    :data="data"
    :columns="columns"
    virtualization
  />
</template>

Virtualization with Features

With Pagination

You typically don't need virtualization with pagination:

<template>
  <!-- Choose one or the other -->

  <!-- Pagination for moderate datasets -->
  <NuGrid :data="data" :columns="columns" :paging="{ pageSize: 50 }" />

  <!-- Virtualization for large datasets without pagination -->
  <NuGrid :data="data" :columns="columns" virtualization />
</template>

With Grouping

Virtualization works with grouping:

<template>
  <NuGrid
    v-model:grouping="grouping"
    :data="data"
    :columns="columns"
    virtualization
  />
</template>

With Row Selection

Selection state is maintained during virtualization:

<template>
  <NuGrid
    v-model:row-selection="rowSelection"
    :data="data"
    :columns="columns"
    virtualization
    selection="multi"
  />
</template>

Scroll Events

Handle scroll events:

<script setup lang="ts">
function onScroll(event) {
  console.log('Scroll position:', event.scrollTop)
}
</script>

<template>
  <NuGrid
    :data="data"
    :columns="columns"
    virtualization
    @scroll="onScroll"
  />
</template>

Smooth Scrolling

NuGrid implements smooth scrolling behavior:

<template>
  <NuGrid
    :data="data"
    :columns="columns"
    virtualization
    :scroll-options="{
      behavior: 'smooth'
    }"
  />
</template>

Benchmarks

Typical performance with virtualization enabled:

RowsInitial RenderScroll Performance
1,000~50ms60fps
10,000~60ms60fps
100,000~80ms60fps

Performance depends on:

  • Column count
  • Cell complexity
  • Browser/device

Troubleshooting

Grid Not Scrolling

Ensure the container has a defined height:

<!-- Won't work - no height constraint -->
<NuGrid :data="data" :columns="columns" virtualization />

<!-- Will work -->
<div class="h-[500px]">
  <NuGrid :data="data" :columns="columns" virtualization />
</div>

Scroll Jumping

If scroll position jumps, try providing an estimated row height:

<template>
  <NuGrid
    :data="data"
    :columns="columns"
    virtualization
    :estimated-row-height="44"
  />
</template>

Rows Not Rendering

Check that data is reactive:

<script setup lang="ts">
// Must be reactive
const data = ref([])

// Load data
data.value = await fetchData()
</script>

Next Steps

Column Sizing

Resize and auto-size columns.

Pagination

Paginate large datasets.