Row sorting

Sorting is a feature you opt into. Try it live - click any column header to cycle none → asc → desc → none; shift-click adds the column to the sort key list (multi-sort):

Open the live example: Sort, filter, paginate (Filtering & Search)

The examples on this page run against these rows:

<script lang="ts">
  import { SvGrid } from '@svgrid/grid'

  type Person = {
    id: number
    name: string
    email: string
    department: string
    age: number
    salary: number
    city: string
    startDate: string
    active: boolean
  }

  const people: Person[] = [
    { id: 1, name: 'Ada Lovelace',   email: '[email protected]',   department: 'Engineering', age: 36, salary: 142000, city: 'London',   startDate: '2021-03-01', active: true },
    { id: 2, name: 'Grace Hopper',   email: '[email protected]', department: 'Engineering', age: 45, salary: 168000, city: 'New York', startDate: '2019-07-15', active: true },
    { id: 3, name: 'Linus Torvalds', email: '[email protected]', department: 'Platform',    age: 54, salary: 155000, city: 'Portland', startDate: '2020-01-20', active: false },
    { id: 4, name: 'Radia Perlman',  email: '[email protected]', department: 'Networking',  age: 49, salary: 161000, city: 'Seattle',  startDate: '2022-09-05', active: true },
    { id: 5, name: 'Barbara Liskov', email: '[email protected]', department: 'Platform',  age: 52, salary: 172000, city: 'Boston',   startDate: '2018-11-11', active: true },
  ]

  const data = people
</script>
<script lang="ts">
  import {
    SvGrid, tableFeatures, rowSortingFeature, type ColumnDef,
  } from '@svgrid/grid'

  const features = tableFeatures({ rowSortingFeature })

  const columns: ColumnDef<typeof features, Person>[] = [
    { field: 'firstName', header: 'First name', editorType: 'text' },
    { field: 'age',       header: 'Age',        editorType: 'number' },
    { field: 'joinedAt',  header: 'Joined',     editorType: 'date' },
  ]
</script>

<SvGrid {data} {columns} features={features} />

Clicking a sortable header toggles none → asc → desc → none. Shift-click adds the column to the sort key list (multi-sort).

Sort functions

sortFns exposes the built-in comparators:

import { sortFns } from '@svgrid/grid'
// sortFns.auto    - blanks, then numbers numerically, then text (default for unknown types)
// sortFns.number  - numeric; blanks and non-numeric values ('n/a') first
// sortFns.date    - Date-parsed; blanks and invalid dates first

All three sort blanks (null, undefined, '') first ascending and last descending, and never return NaN, so a column with placeholder text still sorts into a consistent order.

The grid picks the comparator based on the column's editorType:

editorType comparator
'number' sortFns.number
'date' | 'datetime' sortFns.date
anything else sortFns.auto

If your column has a non-trivial type, set editorType even if you do not want inline editing - it is what tells sort and filter how to behave.

Sort by a rank

A status column sorted as text puts "Done" before "To do". sortValue gives the number to sort a value by instead, and grouped rows follow it too:

const STATUS = ['To do', 'In progress', 'Done']
const columns = [
  { field: 'status', header: 'Status', sortValue: (v: unknown) => STATUS.indexOf(String(v)) },
]

Blanks, and anything that is not a number, sort first ascending, as in a number column.

Your own comparator

sortComparator compares two of a column's values in place of the built-in order. It also gets the direction being sorted, desc. The grid reverses your result for a descending sort, so return the ascending order; desc is there for a value you want held at one end both ways. sortValue wins when a column sets both.

const columns = [
  { field: 'code', header: 'Code', sortComparator: (a, b) => String(a).length - String(b).length },
]

Keep blanks at one end (Enterprise)

@svgrid/enterprise ships comparators that hold blank cells at the bottom or the top in both directions, so tasks with no due date stay below the dated ones whichever way Due runs. The other values sort as the grid sorts them (setup):

// blanksLast is exported by @svgrid/enterprise
const columns = [
  { field: 'due', header: 'Due', cellDataType: 'dateString', sortComparator: blanksLast('date') },
]

blanksFirst holds them at the top. Pass 'number', 'date' or 'auto' (the default) for how the other values compare.

Programmatic sort

api.setSort('age', 'desc')   // sort by age descending
api.setSort('age', null)     // clear sort on this column
api.clearSort()              // clear all sort

setSort replaces any existing sort with a single clause. For multi-column sort, the user clicks the second column header with Shift held (the in-grid affordance). A multi-sort setter on the API is tracked in Missing features.

To observe sort changes from outside the grid:

<SvGrid
  data={rows}
  columns={columns}
  features={features}
  onSortingChange={(next) => (sorting = next)}
/>

Disable sort per column

Not yet first-class - there is no enableSorting: false field. To prevent a column from being sortable, omit rowSortingFeature from tableFeatures(...), or render a custom header via header: () => renderSnippet(...) that doesn't bind to the sort button.

Sorting + server-side data

When sort happens on the backend, pair externalSort={true} with onSortingChange and round-trip the sort clauses through your fetcher. See the 09-server-side demo.

Multi-column sort

Shift-click a second header and the sort becomes ordered: department first, then salary within it. The order you click is the order applied, which is why the header shows a position number once more than one is active.

<script lang="ts">
  import { SvGrid, type GridColumns, type SvGridApi } from '@svgrid/grid'

  type Person = {
    id: number
    name: string
    department: string
    city: string
    salary: number
    bio: string
  }

  const seed: Person[] = [
    { id: 1, name: 'Ada Lovelace',   department: 'Engineering', city: 'London',   salary: 142000,
      bio: 'Wrote the first algorithm intended for a machine, and the first account of what a general-purpose computer could do beyond arithmetic.' },
    { id: 2, name: 'Grace Hopper',   department: 'Engineering', city: 'New York', salary: 168000,
      bio: 'Built the first compiler and argued that programs should be written in something closer to English than to machine code.' },
    { id: 3, name: 'Linus Torvalds', department: 'Platform',    city: 'Portland', salary: 155000,
      bio: 'Wrote a kernel and, later, the version control system that most of the industry now runs on.' },
  ]

  const columns: GridColumns<Person> = [
    { field: 'department', header: 'Department', width: 170 },
    { field: 'name',       header: 'Name',       width: 180 },
    { field: 'salary',     header: 'Salary',     width: 140,
      format: { type: 'currency', currency: 'USD' } },
  ]
</script>

<SvGrid data={seed} {columns} sortable />

Reading the sort model

onSortingChange reports the model as an ordered array, which is exactly the shape you persist to a URL or hand to a server.

<script lang="ts">
  import { SvGrid, type GridColumns, type SvGridApi } from '@svgrid/grid'

  type Person = {
    id: number
    name: string
    department: string
    city: string
    salary: number
    bio: string
  }

  const seed: Person[] = [
    { id: 1, name: 'Ada Lovelace',   department: 'Engineering', city: 'London',   salary: 142000,
      bio: 'Wrote the first algorithm intended for a machine, and the first account of what a general-purpose computer could do beyond arithmetic.' },
    { id: 2, name: 'Grace Hopper',   department: 'Engineering', city: 'New York', salary: 168000,
      bio: 'Built the first compiler and argued that programs should be written in something closer to English than to machine code.' },
    { id: 3, name: 'Linus Torvalds', department: 'Platform',    city: 'Portland', salary: 155000,
      bio: 'Wrote a kernel and, later, the version control system that most of the industry now runs on.' },
  ]

  let model = $state('[]')

  const columns: GridColumns<Person> = [
    { field: 'name',   header: 'Name',   width: 190 },
    { field: 'city',   header: 'City',   width: 150 },
    { field: 'salary', header: 'Salary', width: 140,
      format: { type: 'currency', currency: 'USD' } },
  ]
</script>

<SvGrid
  data={seed}
  {columns}
  sortable
  onSortingChange={(s) => (model = JSON.stringify(s))}
/>

<p>Sort model: <code>{model}</code></p>

See also

Live examples

Related articles