InfiniteTable comes with multiple sorting behaviours, which are described below.

Single and Multiple Sorting

Both single and multiple sorting are supported via the sortInfo and defaultSortInfo props.

Single Sorting

For single sorting, sortInfo (or the uncontrolled defaultSortInfo) should an object like

// sort by `firstName`, in ascending order
sortInfo = { field: 'firstName', dir: 1 };

or you can use

// no sorting
sortInfo = null;

for explicit no sorting.

Note

When you use controlled sorting via sortInfo, make sure you also listen to onSortInfoChange for changes, to get notifications when sorting is changed by the user. Also, for controlled sorting, it’s your responsibility to sort the data - read bellow in the controlled and uncontrolled section.

The sort information object has the following shape:

  • dir - 1 | -1 - the direction of the sorting
  • field? - keyof DATA_TYPE - the field to sort by - optional.
  • id? - string - if you don’t sort by a field, you can specify an id of the column this sorting is bound to. Note that columns have a valueGetter, which will be used when doing local sorting and the column is not bound to an exact field.
  • type? - the sort type - one of the keys in sortTypes - eg "string", "number" - will be used for local sorting, to provide the proper comparison function.
Local + uncontrolled single-sorting example

This example shows initial sorting by salary in ascending order. Click the header of the salary column to sort in descending order and then click it again to unsort.

View Mode
Fork
import { InfiniteTable, DataSource } from '@infinite-table/infinite-react';
import type { InfiniteTablePropColumns } from '@infinite-table/infinite-react';
import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;

  email: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const columns: InfiniteTablePropColumns<Developer> = {
  preferredLanguage: { field: 'preferredLanguage' },
  salary: {
    field: 'salary',
    type: 'number',
  },
  age: { field: 'age' },
  canDesign: { field: 'canDesign' },
  country: { field: 'country' },
  firstName: { field: 'firstName' },
  stack: { field: 'stack' },
  id: { field: 'id' },
  hobby: { field: 'hobby' },
  city: { field: 'city' },
  currency: { field: 'currency' },
};

export default function LocalUncontrolledSingleSortingExample() {
  return (
    <>
      <DataSource<Developer>
        primaryKey="id"
        data={dataSource}
        defaultSortInfo={{
          field: 'salary',
          dir: 1,
        }}
      >
        <InfiniteTable<Developer> columns={columns} columnDefaultWidth={220} />
      </DataSource>
    </>
  );
}

const dataSource: Developer[] = [
  {
    id: 0,

    firstName: 'Nya',
    lastName: 'Klein',
    country: 'India',
    city: 'Unnao',
    age: 24,
    currency: 'JPY',
    preferredLanguage: 'TypeScript',
    stack: 'backend',
    canDesign: 'yes',
    salary: 60000,
    hobby: 'sports',
    email: 'Nya44@gmail.com',
  },
  {
    id: 1,
    firstName: 'Axel',
    lastName: 'Runolfsson',
    country: 'Mexico',
    city: 'Cuitlahuac',
    age: 46,
    currency: 'USD',
    preferredLanguage: 'TypeScript',
    stack: 'backend',
    canDesign: 'no',
    salary: 100000,
    hobby: 'sports',
    email: 'Axel93@hotmail.com',
  },
  {
    id: 2,
    firstName: 'Gonzalo',
    lastName: 'McGlynn',
    country: 'United Arab Emirates',
    city: 'Fujairah',
    age: 54,
    currency: 'JPY',
    preferredLanguage: 'Go',
    stack: 'frontend',
    canDesign: 'yes',
    salary: 120000,
    hobby: 'photography',
    email: 'Gonzalo_McGlynn34@gmail.com',
  },
  {
    id: 3,
    firstName: 'Sherwood',
    lastName: 'McLaughlin',
    country: 'Mexico',
    city: 'Tlacolula de Matamoros',
    age: 43,
    currency: 'CHF',
    preferredLanguage: 'Rust',
    stack: 'backend',
    canDesign: 'no',
    salary: 99000,
    hobby: 'cooking',
    email: 'Sherwood_McLaughlin65@hotmail.com',
  },
  {
    id: 4,
    firstName: 'Alexandre',
    lastName: 'Harber',
    country: 'France',
    city: 'Persan',
    age: 23,
    currency: 'EUR',
    preferredLanguage: 'Go',
    stack: 'backend',
    canDesign: 'yes',
    salary: 97000,
    hobby: 'reading',
    email: 'Alexandre_Harber@hotmail.com',
  },
  {
    id: 5,

    firstName: 'Mariane',
    lastName: 'Schroeder',
    country: 'United States',
    city: 'Hays',
    age: 34,
    currency: 'EUR',
    preferredLanguage: 'TypeScript',
    stack: 'backend',
    canDesign: 'no',
    salary: 58000,
    hobby: 'cooking',
    email: 'Mariane0@hotmail.com',
  },
  {
    id: 6,
    firstName: 'Rosalind',
    lastName: 'Mills',
    country: 'Mexico',
    city: 'Nuevo Casas Grandes',
    age: 33,
    currency: 'AUD',
    preferredLanguage: 'JavaScript',
    stack: 'frontend',
    canDesign: 'no',
    salary: 198000,
    hobby: 'dancing',
    email: 'Rosalind69@gmail.com',
  },
  {
    id: 7,
    firstName: 'Lolita',
    lastName: 'Hayes',
    country: 'Sweden',
    city: 'Delsbo',
    age: 22,
    currency: 'JPY',
    preferredLanguage: 'TypeScript',
    stack: 'full-stack',
    canDesign: 'yes',
    salary: 200000,
    hobby: 'cooking',
    email: 'Lolita.Hayes@hotmail.com',
  },
  {
    id: 8,
    firstName: 'Tre',
    lastName: 'Boyle',
    country: 'Germany',
    city: 'Bad Camberg',
    age: 11,
    currency: 'GBP',
    preferredLanguage: 'TypeScript',
    stack: 'backend',
    canDesign: 'yes',
    salary: 200000,
    hobby: 'sports',
    email: 'Tre28@gmail.com',
  },
  {
    id

Multiple Sorting

If you want to use multiple sorting, specify an array of objects like

// sort by age in descending order, then by `firstName` in ascending order
sortInfo = [
  { field: 'age', type: 'number', dir: -1 },
  { field: 'firstName', dir: 1 },
];

// no sorting
sortInfo = [];

This allows sorting by multiple fields (to which columns are bound) - you can specify however many you want - so when sorting two objects in the DataSource, the first sortInfo is used to compare the two, and then, on equal values, the next sortInfo is used and so on.

Local + uncontrolled multi-sorting example

This table allows sorting multiple columns - initially the country column is sorted in descending order and the salary column is sorted in ascending order. Click the salary column to toggle the column sort to descending. Clicking it a second time will remove it from the sort altogether.

View Mode
Fork
import {
  InfiniteTable,
  DataSource,
  DataSourceData,
} from '@infinite-table/infinite-react';
import type { InfiniteTablePropColumns } from '@infinite-table/infinite-react';
import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource: DataSourceData<Developer> = () => {
  return fetch('https://infinite-table.com/.netlify/functions/json-server' + `/developers100-sql?`)
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  preferredLanguage: { field: 'preferredLanguage' },
  country: { field: 'country' },
  salary: {
    field: 'salary',
    type: 'number',
  },
  age: { field: 'age' },
  canDesign: { field: 'canDesign' },
  firstName: { field: 'firstName' },
  stack: { field: 'stack' },
  id: { field: 'id' },
  hobby: { field: 'hobby' },
  city: { field: 'city' },
  currency: { field: 'currency' },
};

const domProps = { style: { height: '90vh' } };

export default function LocalUncontrolledMultiSortingExampleWithRemoteData() {
  return (
    <>
      <DataSource<Developer>
        primaryKey="id"
        data={dataSource}
        defaultSortInfo={[
          { field: 'country', dir: -1 },
          { field: 'salary', dir: 1 },
        ]}
        sortMode="local"
      >
        <InfiniteTable<Developer>
          domProps={domProps}
          columns={columns}
          columnDefaultWidth={220}
        />
      </DataSource>
    </>

Remote + uncontrolled multi-sorting example
View Mode
Fork
import {
  InfiniteTable,
  DataSource,
  DataSourceData,
} from '@infinite-table/infinite-react';
import type { InfiniteTablePropColumns } from '@infinite-table/infinite-react';
import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource: DataSourceData<Developer> = ({ sortInfo }) => {
  if (sortInfo && !Array.isArray(sortInfo)) {
    sortInfo = [sortInfo];
  }
  const args = [
    sortInfo
      ? 'sortInfo=' +
        JSON.stringify(
          sortInfo.map((s) => ({
            field: s.field,
            dir: s.dir,
          })),
        )
      : null,
  ]
    .filter(Boolean)
    .join('&');

  return fetch('https://infinite-table.com/.netlify/functions/json-server' + `/developers100-sql?` + args)
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  preferredLanguage: { field: 'preferredLanguage' },
  salary: {
    field: 'salary',
    type: 'number',
  },
  age: { field: 'age' },
  canDesign: { field: 'canDesign' },
  country: { field: 'country' },
  firstName: { field: 'firstName' },
  stack: { field: 'stack' },
  id: { field: 'id' },
  hobby: { field: 'hobby' },
  city: { field: 'city' },
  currency: { field: 'currency' },
};

export default function RemoteUncontrolledMultiSortingExample() {
  return (
    <>
      <DataSource<Developer>
        primaryKey="id"
        data={dataSource}
        defaultSortInfo={[
          {
            field: 'salary',
            dir: -1,
          },
        ]}
        sortMode="remote"
      >
        <InfiniteTable<Developer> columns={columns} columnDefaultWidth={220} />
      </DataSource>
    </>

Note

If you use uncontrolled sorting via defaultSortInfo there’s no way to switch between single and multiple sorting after the component is mounted. If you have this use-case, you need to use the controlled sortInfo prop.

Controlled and Uncontrolled Sorting

  • uncontrolled (embedded) sorting - the <DataSource /> sorts the data internally (when sortMode=local, which is the default), based on the defaultSortInfo prop. As the user interacts with the table, onSortInfoChange is being called with updated sort info and the <DataSource /> continues to sort the data accordingly.

Note

The defaultSortInfo prop is an uncontrolled prop, so it’s all managed inside the <DataSource /> component and you can’t change it from the outside. If you need to control it from outside the component, use the controlled sortInfo prop.

  • controlled (outside) sorting - the <DataSource /> doesn’t sort the data internally, but instead it expects the data it is provided with to be already sorted. In this case you need to use the controlled sortInfo prop and pass it to <DataSource /> so it knows the current sort order, which is needed for displaying correct sort indicators on column headers and which should be the next sort order when the user interacts with the table.

Both controlled sortInfo and uncontrolled defaultSortInfo work in combination with onSortInfoChange - use it to be notified when sorting changes, so you can react and update your app accordingly if needed.

Local and Remote Sorting

Sorting can be done both locally in the browser and remotely on the server. For configuring where sorting is being performed, use the sortMode prop - it can be either local or remote (defaults to local).

Note

Controlled sorting (via sortInfo) means sorting is done outside of the <DataSource /> component, so it’s up to you to sort the data.

This is also the case for sortMode=remote, so the two are very similar.

Local Sorting

When you use uncontrolled sorting locally, the <DataSource /> will sort the data internally, based on the defaultSortInfo prop. Local sorting is available for any configured data source - be it an array or a function that returns a promise.

Note

You can use onDataParamsChange, which is called whenever any of the sorting, filtering, grouping or pivoting information changes.

Local uncontrolled sorting + local data
View Mode
Fork
import {
  InfiniteTable,
  DataSource,
  DataSourceData,
} from '@infinite-table/infinite-react';
import type { InfiniteTablePropColumns } from '@infinite-table/infinite-react';
import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource: DataSourceData<Developer> = () => {
  return fetch('https://infinite-table.com/.netlify/functions/json-server' + `/developers100-sql?`)
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  preferredLanguage: { field: 'preferredLanguage' },
  salary: {
    field: 'salary',
    type: 'number',
  },
  age: { field: 'age' },

  canDesign: { field: 'canDesign' },
  country: { field: 'country' },
  firstName: { field: 'firstName' },
  stack: { field: 'stack' },
  id: { field: 'id' },
  hobby: { field: 'hobby' },
  city: { field: 'city' },
  currency: { field: 'currency' },
};

export default function LocalUncontrolledSingleSortingExampleWithRemoteData() {
  return (
    <>
      <DataSource<Developer>
        primaryKey="id"
        data={dataSource}
        defaultSortInfo={{ field: 'salary', dir: -1 }}
        sortMode="local"
      >
        <InfiniteTable<Developer> columns={columns} columnDefaultWidth={220} />
      </DataSource>
    </>

Local uncontrolled sorting + remote data
View Mode
Fork
import {
  InfiniteTable,
  DataSource,
  DataSourceData,
} from '@infinite-table/infinite-react';
import type { InfiniteTablePropColumns } from '@infinite-table/infinite-react';
import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource: DataSourceData<Developer> = () => {
  return fetch('https://infinite-table.com/.netlify/functions/json-server' + `/developers100-sql?`)
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  preferredLanguage: { field: 'preferredLanguage' },
  salary: {
    field: 'salary',
    type: 'number',
  },
  age: { field: 'age' },

  canDesign: { field: 'canDesign' },
  country: { field: 'country' },
  firstName: { field: 'firstName' },
  stack: { field: 'stack' },
  id: { field: 'id' },
  hobby: { field: 'hobby' },
  city: { field: 'city' },
  currency: { field: 'currency' },
};

export default function LocalUncontrolledSingleSortingExampleWithRemoteData() {
  return (
    <>
      <DataSource<Developer>
        primaryKey="id"
        data={dataSource}
        defaultSortInfo={{ field: 'salary', dir: -1 }}
        sortMode="local"
      >
        <InfiniteTable<Developer> columns={columns} columnDefaultWidth={220} />
      </DataSource>
    </>

Remote Sorting

Sorting remotely makes a lot of sense when using a function as your data source. Whenever the sort information is changed, the function will be called with all the information needed to retrieve the data from the remote endpoint.

Note

For remote sorting, make sure you specify sortMode=“remote” - if you don’t, the data will also be sorted locally in the browser (which most of the times will be harmless, but it means wasted CPU cycles).

Remote + controlled multi-sorting example
View Mode
Fork
import {
  InfiniteTable,
  DataSource,
  DataSourceData,
  DataSourcePropSortInfo,
} from '@infinite-table/infinite-react';
import type { InfiniteTablePropColumns } from '@infinite-table/infinite-react';
import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource: DataSourceData<Developer> = ({ sortInfo }) => {
  if (sortInfo && !Array.isArray(sortInfo)) {
    sortInfo = [sortInfo];
  }
  const args = [
    sortInfo
      ? 'sortInfo=' +
        JSON.stringify(
          sortInfo.map((s) => ({
            field: s.field,
            dir: s.dir,
          })),
        )
      : null,
  ]
    .filter(Boolean)
    .join('&');

  return fetch('https://infinite-table.com/.netlify/functions/json-server' + `/developers100-sql?` + args)
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  preferredLanguage: { field: 'preferredLanguage' },
  salary: {
    field: 'salary',
    type: 'number',
  },
  age: { field: 'age' },
  canDesign: { field: 'canDesign' },
  country: { field: 'country' },
  firstName: { field: 'firstName' },
  stack: { field: 'stack' },
  id: { field: 'id' },
  hobby: { field: 'hobby' },
  city: { field: 'city' },
  currency: { field: 'currency' },
};

export default function RemoteControlledMultiSortingExample() {
  const [sortInfo, setSortInfo] = React.useState<
    DataSourcePropSortInfo<Developer>
  >([
    {
      field: 'salary',
      dir: -1,
    },
  ]);
  return (
    <>
      <DataSource<Developer>
        primaryKey="id"
        data={dataSource}
        sortInfo={sortInfo}
        sortMode="remote"
        onSortInfoChange={setSortInfo}
      >
        <InfiniteTable<Developer> columns={columns} columnDefaultWidth={220} />
      </DataSource>
    </>

In the example above, remote and controlled sorting are combined - because sortMode="remote" is specified, the <DataSource /> will call the data function whenever sorting changes, and will pass in the dataParams object that contains the sort information.

Custom Sort Functions with sortTypes

By default, all columns are sorted as strings, even if they contain numeric values. To make numeric columns sort as numbers, you need to specify a dataType for the column, or, a column sortType.

There are two dataType values that can be used:

  • "string"
  • "number"

Each dataType has its own sorting function and its own filtering operators & functions.

Sorting works in combination with the sortTypes property, which is an object with keys being sort types and values being functions that compare two values of the same type.

const sortTypes = {
  string: (a, b) => a.localeCompare(b),
  number: (a, b) => a - b,
};

Those are the two sort types supported by default.

Note

A column can choose to use a specific columns.sortType, in which case, for local sorting, the corresponding sort function will be used, or, it can simply specify a dataType and the sortType with the same name will be used (when no explicit sortType is defined).

To conclude, the dataType of a column will be used as the sortType and filterType, when those are not explicitly specified.

Custom sort by color - magenta will come first
View Mode
Fork
import {
  InfiniteTable,
  DataSource,
  InfiniteTableColumn,
} from '@infinite-table/infinite-react';
import * as React from 'react';

export type CarSale = {
  id: number;

  make: string;
  model: string;
  year: number;

  sales: number;
  color: string;
};

const carsales: CarSale[] = [
  {
    make: 'Volkswagen',
    model: 'GTI',
    year: 2009,
    sales: 6,
    color: 'red',
    id: 0,
  },
  {
    make: 'Honda',
    model: 'Element 2WD',
    year: 2009,
    sales: 739,
    color: 'red',
    id: 1,
  },
  {
    make: 'Acura',
    model: 'RDX 4WD',
    year: 2008,
    sales: 2,
    color: 'magenta',
    id: 2,
  },
  {
    make: 'Honda',
    model: 'Fit',
    year: 2009,
    sales: 211,
    color: 'blue',
    id: 3,
  },
  {
    make: 'Mazda',
    model: '6',
    year: 2009,
    sales: 31,
    color: 'blue',

    id: 4,
  },
  {
    make: 'Acura',
    model: 'TSX',
    year: 2009,
    sales: 14,
    color: 'yellow',
    id: 5,
  },
  {
    make: 'Acura',
    model: 'TSX',
    year: 2010,
    sales: 14,
    color: 'red',
    id: 6,
  },
  {
    make: 'Audi',
    model: 'A3',
    year: 2009,
    sales: 2,
    color: 'magenta',
    id: 7,
  },
];

const columns: Record<string, InfiniteTableColumn<CarSale>> = {
  color: { field: 'color', sortType: 'color' },
  make: { field: 'make' },
  model: { field: 'model' },

  sales: {
    field: 'sales',
    sortType: 'number',
  },
  year: {
    field: 'year',
    sortType: 'number',
  },
};
const newSortTypes = {
  color: (one: string, two: string) => {
    if (one === 'magenta') {
      // magenta comes first
      return -1;
    }
    if (two === 'magenta') {
      // magenta comes first
      return 1;
    }
    return one.localeCompare(two);
  },
};

export default function DataTestPage() {
  return (
    <>
      <DataSource<CarSale>
        data={carsales}
        primaryKey="id"
        defaultSortInfo={{
          field: 'color',
          dir: 1,
          type: 'color',
        }}
        sortTypes={newSortTypes}
      >
        <InfiniteTable<CarSale> columns={columns} />
      </DataSource>
    </>

Note

In this example, for the "color" column, we specified column.sortType=“color” - we could have passed that as column.dataType instead, but if the grid had filtering, it wouldn’t know what filters to use for “color” - so we usedcolumn.sortType to only change how the data is sorted.

Note

When you provide a defaultSortInfo prop and the sorting information uses a custom sortType, make sure you specify that as the type property of the sorting info object.

defaultSortInfo={{
  field: 'color',
  dir: 1,
  // note this custom sort type
  type: 'color',
}}

You will need to have a property for that type in your sortTypes object as well.

sortTypes={{
  color: (a, b) => //...
}}