Layout & Display

Pagination

Paginate large datasets in NuGrid.

NuGrid provides built-in pagination for dividing large datasets into manageable pages.

ID
Name
Category
Value
1
Item 1
Electronics
$844
2
Item 2
Clothing
$850
3
Item 3
Books
$59
4
Item 4
Home
$843
5
Item 5
Electronics
$558
6
Item 6
Clothing
$888
7
Item 7
Books
$382
8
Item 8
Home
$720
9
Item 9
Electronics
$714
10
Item 10
Clothing
$595
<script setup lang="ts">
import type { NuGridColumn } from '#nu-grid/types'

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

// Generate sample data
const data = ref<Item[]>(
  Array.from({ length: 50 }, (_, i) => ({
    id: i + 1,
    name: `Item ${i + 1}`,
    category: ['Electronics', 'Clothing', 'Books', 'Home'][i % 4]!,
    value: Math.floor(Math.random() * 1000) + 10,
  })),
)

const columns: NuGridColumn<Item>[] = [
  { accessorKey: 'id', header: 'ID', size: 60 },
  { accessorKey: 'name', header: 'Name', size: 120 },
  { accessorKey: 'category', header: 'Category', size: 120 },
  {
    accessorKey: 'value',
    header: 'Value',
    size: 100,
    cell: ({ row }) => `$${row.original.value}`,
  },
]
</script>

<template>
  <div class="w-full">
    <NuGrid
      :data="data"
      :columns="columns"
      :paging="{
        enabled: true,
        pageSize: 10,
        pageSizeSelector: [5, 10, 20, 50],
      }"
      :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>
</template>

Enabling Pagination

Enable pagination with the paging prop:

<template>
  <!-- Simple enable with defaults -->
  <NuGrid :data="data" :columns="columns" :paging="true" />

  <!-- With options -->
  <NuGrid
    :data="data"
    :columns="columns"
    :paging="{
      pageSize: 20,
      pageSizeSelector: [10, 20, 50, 100],
    }"
  />
</template>

Paging Options

interface NuGridPagingOptions {
  enabled?: boolean
  pageSize?: number
  pageSizeSelector?: number[] | false
  autoPageSize?: boolean
  suppressPanel?: boolean
}

Options Reference

OptionTypeDefaultDescription
enabledbooleanfalseEnable pagination
pageSizenumber20Rows per page
pageSizeSelectornumber[] | false[10, 20, 50, 100]Page size options
autoPageSizebooleanfalseAuto-fit rows to container
suppressPanelbooleanfalseHide built-in pagination UI

Pagination State

Track pagination state with v-model:

<script setup lang="ts">
const pagination = ref({
  pageIndex: 0,
  pageSize: 20,
})
</script>

<template>
  <NuGrid
    v-model:pagination="pagination"
    :data="data"
    :columns="columns"
    :paging="true"
  />
</template>

Page Size Selector

Customize available page sizes:

<template>
  <!-- Custom options -->
  <NuGrid
    :data="data"
    :columns="columns"
    :paging="{
      pageSize: 25,
      pageSizeSelector: [25, 50, 100, 200],
    }"
  />

  <!-- Hide selector -->
  <NuGrid
    :data="data"
    :columns="columns"
    :paging="{
      pageSize: 20,
      pageSizeSelector: false,
    }"
  />
</template>

Auto Page Size

Automatically calculate page size based on container height:

<template>
  <div class="h-[500px]">
    <NuGrid
      :data="data"
      :columns="columns"
      :paging="{ autoPageSize: true }"
    />
  </div>
</template>

The grid will automatically adjust the page size to fill the available space.

Custom Pagination UI

Hide the built-in panel and use your own:

<script setup lang="ts">
const gridRef = useTemplateRef('grid')
const pagination = ref({ pageIndex: 0, pageSize: 20 })

const totalPages = computed(() =>
  Math.ceil(data.value.length / pagination.value.pageSize)
)

function goToPage(page: number) {
  pagination.value.pageIndex = page
}
</script>

<template>
  <NuGrid
    ref="grid"
    v-model:pagination="pagination"
    :data="data"
    :columns="columns"
    :paging="{ suppressPanel: true }"
  />

  <div class="mt-4 flex items-center justify-between">
    <span>
      Page {{ pagination.pageIndex + 1 }} of {{ totalPages }}
    </span>

    <div class="flex gap-2">
      <UButton
        :disabled="pagination.pageIndex === 0"
        @click="goToPage(0)"
      >
        First
      </UButton>
      <UButton
        :disabled="pagination.pageIndex === 0"
        @click="goToPage(pagination.pageIndex - 1)"
      >
        Previous
      </UButton>
      <UButton
        :disabled="pagination.pageIndex >= totalPages - 1"
        @click="goToPage(pagination.pageIndex + 1)"
      >
        Next
      </UButton>
      <UButton
        :disabled="pagination.pageIndex >= totalPages - 1"
        @click="goToPage(totalPages - 1)"
      >
        Last
      </UButton>
    </div>
  </div>
</template>

Programmatic Navigation

Navigate pages via the grid ref:

<script setup lang="ts">
const gridRef = useTemplateRef('grid')

function goToFirstPage() {
  gridRef.value?.pagingGoToPage(0)
}

function goToLastPage() {
  gridRef.value?.pagingGoToPage(-1)  // -1 = last page
}

function goToPage(page: number) {
  gridRef.value?.pagingGoToPage(page)
}
</script>

Keyboard Navigation

When pagination is enabled:

KeyAction
Ctrl/Cmd + Arrow UpPrevious page (with cmdArrows: 'paging')
Ctrl/Cmd + Arrow DownNext page (with cmdArrows: 'paging')
Page UpPrevious page
Page DownNext page

Enable paging keyboard mode:

<template>
  <NuGrid
    :data="data"
    :columns="columns"
    :paging="true"
    :focus="{ cmdArrows: 'paging' }"
  />
</template>

Pagination with Grouping

Pagination works with grouped data:

<script setup lang="ts">
const grouping = ref(['category'])
</script>

<template>
  <NuGrid
    v-model:grouping="grouping"
    :data="data"
    :columns="columns"
    :paging="{ pageSize: 20 }"
  />
</template>

Groups are expanded/collapsed across pages.

Server-Side Pagination

For server-side pagination, manage state externally:

<script setup lang="ts">
const pagination = ref({ pageIndex: 0, pageSize: 20 })
const totalRows = ref(0)
const data = ref([])

async function fetchPage() {
  const response = await api.getItems({
    page: pagination.value.pageIndex,
    limit: pagination.value.pageSize,
  })

  data.value = response.items
  totalRows.value = response.total
}

watch(pagination, fetchPage, { deep: true, immediate: true })
</script>

<template>
  <NuGrid
    v-model:pagination="pagination"
    :data="data"
    :columns="columns"
    :paging="{ suppressPanel: true }"
    :row-count="totalRows"
    manual-pagination
  />

  <!-- Custom pagination controls -->
</template>

Example: Complete Pagination Setup

<script setup lang="ts">
const gridRef = useTemplateRef('grid')
const data = ref(generateData(200))  // 200 items

const paginationOptions = computed(() => ({
  enabled: true,
  pageSize: 20,
  pageSizeSelector: [10, 20, 50, 100],
}))

const currentPage = computed(() =>
  (gridRef.value?.tableApi?.getState().pagination.pageIndex ?? 0) + 1
)

const totalPages = computed(() =>
  gridRef.value?.tableApi?.getPageCount() ?? 1
)
</script>

<template>
  <div class="space-y-4">
    <div class="flex items-center justify-between">
      <span class="text-sm text-muted">
        Showing page {{ currentPage }} of {{ totalPages }}
        ({{ data.length }} total items)
      </span>

      <div class="flex gap-2">
        <UButton
          size="sm"
          variant="outline"
          @click="gridRef?.pagingGoToPage(0)"
        >
          First
        </UButton>
        <UButton
          size="sm"
          variant="outline"
          @click="gridRef?.pagingGoToPage(currentPage - 2)"
        >
          Prev
        </UButton>
        <UButton
          size="sm"
          variant="outline"
          @click="gridRef?.pagingGoToPage(currentPage)"
        >
          Next
        </UButton>
        <UButton
          size="sm"
          variant="outline"
          @click="gridRef?.pagingGoToPage(-1)"
        >
          Last
        </UButton>
      </div>
    </div>

    <NuGrid
      ref="grid"
      :data="data"
      :columns="columns"
      :paging="paginationOptions"
    />
  </div>
</template>

Next Steps

Grouping

Group rows by columns.

Virtualization

Handle large datasets efficiently.