Essentials

Sorting & Filtering

Sort and filter data in NuGrid.

NuGrid provides built-in sorting and filtering capabilities powered by TanStack Table.

Click on the Name column header to toggle sorting.

ID
Email
Age
1
Alice Johnson
alice@example.com
28
2
Bob Smith
bob@example.com
35
3
Carol White
carol@example.com
42
4
David Brown
david@example.com
31
5
Emma Davis
emma@example.com
26
<script setup lang="ts">
import type { NuGridColumn } from '#nu-grid/types'

interface User {
  id: number
  name: string
  email: string
  age: number
}

const data = ref<User[]>([
  { id: 1, name: 'Alice Johnson', email: 'alice@example.com', age: 28 },
  { id: 2, name: 'Bob Smith', email: 'bob@example.com', age: 35 },
  { id: 3, name: 'Carol White', email: 'carol@example.com', age: 42 },
  { id: 4, name: 'David Brown', email: 'david@example.com', age: 31 },
  { id: 5, name: 'Emma Davis', email: 'emma@example.com', age: 26 },
])

const UButton = resolveComponent('UButton')

const columns: NuGridColumn<User>[] = [
  { accessorKey: 'id', header: 'ID', size: 60, enableSorting: true },
  {
    accessorKey: 'name',
    header: ({ column }) => {
      const isSorted = column.getIsSorted()
      return h(UButton, {
        color: 'neutral',
        variant: 'ghost',
        label: 'Name',
        icon: isSorted
          ? isSorted === 'asc'
            ? 'i-lucide-arrow-up-narrow-wide'
            : 'i-lucide-arrow-down-wide-narrow'
          : 'i-lucide-arrow-up-down',
        class: '-mx-2.5',
        onClick: () => column.toggleSorting(column.getIsSorted() === 'asc'),
      })
    },
    size: 150,
    enableSorting: true,
  },
  { accessorKey: 'email', header: 'Email', size: 200, enableSorting: true },
  { accessorKey: 'age', header: 'Age', size: 80, enableSorting: true },
]

const sorting = ref([{ id: 'name', desc: false }])
</script>

<template>
  <div class="w-full">
    <p class="mb-3 text-sm text-muted">Click on the Name column header to toggle sorting.</p>
    <NuGrid
      v-model:sorting="sorting"
      :data="data"
      :columns="columns"
      :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>

Sorting

Basic Sorting

Sorting is enabled by default. Click column headers to sort:

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

Sort State

Track and control sort state with v-model:

<script setup lang="ts">
import type { SortingState } from '@tanstack/vue-table'

const sorting = ref<SortingState>([])

// Pre-sort by name ascending
const sorting = ref<SortingState>([
  { id: 'name', desc: false }
])
</script>

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

Multi-Column Sorting

Hold Shift and click to sort by multiple columns:

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

Disable Sorting Per Column

const columns: NuGridColumn<User>[] = [
  {
    accessorKey: 'actions',
    header: 'Actions',
    enableSorting: false,  // Disable sorting for this column
  },
]

Custom Sort Function

Provide a custom sort function for complex sorting:

const columns: NuGridColumn<User>[] = [
  {
    accessorKey: 'status',
    header: 'Status',
    sortingFn: (rowA, rowB, columnId) => {
      const order = { active: 0, pending: 1, inactive: 2 }
      return order[rowA.original.status] - order[rowB.original.status]
    },
  },
]

Sort Changed Event

Listen to sort changes:

<script setup lang="ts">
function onSortChanged(event) {
  console.log('Sort changed:', event.sorting)
}
</script>

<template>
  <NuGrid
    :data="data"
    :columns="columns"
    @sort-changed="onSortChanged"
  />
</template>

Filtering

Column Filters

Set up column filters:

<script setup lang="ts">
import type { ColumnFiltersState } from '@tanstack/vue-table'

const columnFilters = ref<ColumnFiltersState>([])
</script>

<template>
  <NuGrid
    v-model:column-filters="columnFilters"
    :data="data"
    :columns="columns"
  />
</template>

External Filter Controls

Build your own filter UI:

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

watch(emailFilter, (value) => {
  gridRef.value?.tableApi?.getColumn('email')?.setFilterValue(value)
})
</script>

<template>
  <div class="mb-4">
    <UInput
      v-model="emailFilter"
      placeholder="Filter by email..."
      icon="i-lucide-search"
    />
  </div>

  <NuGrid
    ref="grid"
    v-model:column-filters="columnFilters"
    :data="data"
    :columns="columns"
  />
</template>

Filter Functions

Specify the filter function per column:

const columns: NuGridColumn<User>[] = [
  {
    accessorKey: 'status',
    header: 'Status',
    filterFn: 'equals',  // Exact match filter
  },
  {
    accessorKey: 'name',
    header: 'Name',
    filterFn: 'includesString',  // Contains filter (default)
  },
]

Available Filter Functions

FunctionDescription
includesStringCase-insensitive contains
includesStringSensitiveCase-sensitive contains
equalsStringCase-insensitive equals
equalsStrict equality
arrIncludesValue in array
arrIncludesAllAll values in array
arrIncludesSomeSome values in array
weakEqualsLoose equality
inNumberRangeWithin numeric range

Custom Filter Function

const columns: NuGridColumn<User>[] = [
  {
    accessorKey: 'salary',
    header: 'Salary',
    filterFn: (row, columnId, filterValue) => {
      const salary = row.getValue(columnId) as number
      const [min, max] = filterValue as [number, number]
      return salary >= min && salary <= max
    },
  },
]

Filter Changed Event

<script setup lang="ts">
function onFilterChanged(event) {
  console.log('Filters changed:', event.columnFilters)
}
</script>

<template>
  <NuGrid
    :data="data"
    :columns="columns"
    @filter-changed="onFilterChanged"
  />
</template>

Global Filter

Filter across all columns:

<script setup lang="ts">
const globalFilter = ref('')
</script>

<template>
  <div class="mb-4">
    <UInput
      v-model="globalFilter"
      placeholder="Search all columns..."
      icon="i-lucide-search"
    />
  </div>

  <NuGrid
    v-model:global-filter="globalFilter"
    :data="data"
    :columns="columns"
  />
</template>

Example: Filter Toolbar

Build a complete filter toolbar:

<script setup lang="ts">
const gridRef = useTemplateRef('grid')
const searchQuery = ref('')
const statusFilter = ref('all')

const statusOptions = [
  { label: 'All', value: 'all' },
  { label: 'Active', value: 'active' },
  { label: 'Pending', value: 'pending' },
  { label: 'Inactive', value: 'inactive' },
]

watch(statusFilter, (value) => {
  const statusColumn = gridRef.value?.tableApi?.getColumn('status')
  if (value === 'all') {
    statusColumn?.setFilterValue(undefined)
  } else {
    statusColumn?.setFilterValue(value)
  }
})
</script>

<template>
  <div class="mb-4 flex gap-4">
    <UInput
      v-model="searchQuery"
      placeholder="Search by email..."
      icon="i-lucide-search"
      class="w-64"
      @update:model-value="gridRef?.tableApi?.getColumn('email')?.setFilterValue($event)"
    />

    <USelect
      v-model="statusFilter"
      :items="statusOptions"
      placeholder="Filter status"
      class="w-40"
    />

    <UButton
      color="neutral"
      variant="outline"
      @click="columnFilters = []; statusFilter = 'all'; searchQuery = ''"
    >
      Clear Filters
    </UButton>
  </div>

  <NuGrid
    ref="grid"
    v-model:column-filters="columnFilters"
    :data="data"
    :columns="columns"
  />
</template>

Server-Side Sorting & Filtering

For server-side operations, disable client-side processing:

<script setup lang="ts">
const sorting = ref([])
const columnFilters = ref([])

// Watch for changes and fetch from server
watch([sorting, columnFilters], async () => {
  await fetchData({
    sorting: sorting.value,
    filters: columnFilters.value,
  })
}, { deep: true })
</script>

<template>
  <NuGrid
    v-model:sorting="sorting"
    v-model:column-filters="columnFilters"
    :data="data"
    :columns="columns"
    manual-sorting
    manual-filtering
  />
</template>

Next Steps

Events

Learn about all grid events.

Cell Editing

Enable inline cell editing.