Sorting
Enable column sorting by adding getSortedRowModel() to your table configuration and tracking sort state with SortingState. Use onSortingChange to update the sorting state when users click column headers. The Table.ColumnSortAction component renders clickable sort indicators in header cells.
For columns with non-standard data types like formatted dates, provide a custom sortingFn in the column definition to handle comparison logic.
Username | Role | Account Status | Last Visited At | Visit Count |
|---|
{
"sorting": []
}
import {useMemo, useState} from "react"
import dayjs from "dayjs"
import {
type ColumnDef,
getCoreRowModel,
getSortedRowModel,
type SortingState,
} from "@qualcomm-ui/core/table"
import {Button} from "@qualcomm-ui/react/button"
import {ProgressRing} from "@qualcomm-ui/react/progress-ring"
import {flexRender, Table, useReactTable} from "@qualcomm-ui/react/table"
import {CodeHighlight} from "@qualcomm-ui/react-mdx/code-highlight"
import {type User, useUserData} from "./use-data"
export function SortingDemo() {
const [sorting, setSorting] = useState<SortingState>([])
const {data = [], isFetching, refetch} = useUserData(20)
// always memoize your data and columns
const userColumns: ColumnDef<User>[] = useMemo(
() => [
{
accessorKey: "username",
header: "Username",
id: "username",
},
{
accessorKey: "role",
header: "Role",
id: "role",
size: 120,
},
{
accessorKey: "accountStatus",
header: "Account Status",
id: "accountStatus",
},
{
accessorKey: "lastVisitedAt",
header: "Last Visited At",
id: "lastVisitedAt",
minSize: 205,
// we override this column's default sorting function for compatibility with
// formatted date strings.
sortingFn: (rowA, rowB, columnId) => {
const valueA: string = rowA.getValue(columnId)
const valueB: string = rowB.getValue(columnId)
return dayjs(valueA).isAfter(dayjs(valueB)) ? 1 : -1
},
},
{
accessorKey: "visitCount",
header: "Visit Count",
id: "visitCount",
},
],
[],
)
const refreshData = () => refetch()
const table = useReactTable({
columns: userColumns,
data,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
onSortingChange: setSorting,
sortDescFirst: true,
state: {
sorting,
},
})
return (
<div className="flex w-full flex-col gap-4 p-2">
<Table.Root>
<Table.ActionBar>
<Button onClick={() => void refreshData()} variant="outline">
Refresh Data
</Button>
{isFetching ? <ProgressRing size="xs" /> : null}
</Table.ActionBar>
<Table.ScrollContainer>
<Table.Table>
<Table.Header>
{table.getHeaderGroups().map((headerGroup) => (
<Table.Row key={headerGroup.id}>
{headerGroup.headers.map((header) => {
if (header.isPlaceholder) {
return (
<Table.HeaderCell
key={header.id}
colSpan={header.colSpan}
style={{width: header.getSize()}}
/>
)
}
return (
<Table.HeaderCell
key={header.id}
colSpan={header.colSpan}
style={{width: header.getSize()}}
>
<div className="inline-flex items-center gap-2">
{flexRender(
header.column.columnDef.header,
header.getContext(),
)}
<Table.ColumnSortAction header={header} />
</div>
</Table.HeaderCell>
)
})}
</Table.Row>
))}
</Table.Header>
<Table.Body>
{table.getRowModel().rows.map((row) => {
return (
<Table.Row key={row.id}>
{row.getVisibleCells().map((cell) => {
return (
<Table.Cell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</Table.Cell>
)
})}
</Table.Row>
)
})}
</Table.Body>
</Table.Table>
</Table.ScrollContainer>
</Table.Root>
<CodeHighlight
className="w-fit"
code={JSON.stringify({sorting: table.getState().sorting}, null, 2)}
disableCopy
language="json"
/>
</div>
)
}