# Sorting

> Docs and examples on sorting the DataSource for Infinite Table DataGrid

Canonical page: https://infinite-table.com/docs/learn/sorting/overview

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

Both [single sorting](https://infinite-table.com/docs/learn/sorting/single-sorting.md) and [multiple sorting](https://infinite-table.com/docs/learn/sorting/multiple-sorting.md) are supported via the [`sortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#sortInfo) and [`defaultSortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#defaultSortInfo) props.

### Single Sorting

For [single sorting](https://infinite-table.com/docs/learn/sorting/single-sorting.md), [`sortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#sortInfo) (or the uncontrolled [`defaultSortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#defaultSortInfo)) should an object like

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

or you can use

```ts
// no sorting
sortInfo = null;
```

for explicit no sorting.

When you use controlled sorting via [`sortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#sortInfo), make sure you also listen to [`onSortInfoChange`](https://infinite-table.com/docs/reference/datasource-props/index.md#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](#controlled-and-uncontrolled-sorting).

The sort information object has the following shape (see [`DataSourceSingleSortInfo`](https://infinite-table.com/docs/reference/type-definitions/index.md#DataSourceSingleSortInfo) for details):

- `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](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.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`](https://infinite-table.com/docs/reference/datasource-props/index.md#sortTypes) - eg `"string"`, `"number"`, `"date"` - will be used for local sorting, to provide the proper comparison function.

**Example: 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.

```ts
import {
  InfiniteTable,
  DataSource,
  DataSourceSingleSortInfo,
} 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> = {
  firstName: { field: 'firstName' },
  age: { field: 'age' },
  country: { field: 'country' },
  preferredLanguage: { field: 'preferredLanguage' },
  salary: {
    field: 'salary',
    type: 'number',
  },
  canDesign: { field: 'canDesign' },
  stack: { field: 'stack' },
  id: { field: 'id' },
  hobby: { field: 'hobby' },
  city: { field: 'city' },
  currency: { field: 'currency' },
};

const defaultSortInfo: DataSourceSingleSortInfo<Developer> = {
  field: 'age',
  dir: 1,
};
export default function LocalUncontrolledSingleSortingExample() {
  return (
    <>
      <DataSource<Developer>
        primaryKey="id"
        data={dataSource}
        defaultSortInfo={defaultSortInfo}
      >
        <InfiniteTable<Developer>
          debugId="local-uncontrolled-single-sorting-example-with-local-data"
          columns={columns}
          columnDefaultWidth={120}
        />
      </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: 9,
    firstName: 'Lurline',
    lastName: 'Deckow',
    country: 'Canada',
    city: 'Raymore',
    age: 31,
    currency: 'EUR',
    preferredLanguage: 'Rust',
    stack: 'frontend',
    canDesign: 'yes',
    salary: 58000,
    hobby: 'sports',
    email: 'Lurline_Deckow@gmail.com',
  },
];
```

By default, columns in the InfiniteTable DataGrid are sortable.

If you want to disable column sorting for all columns, use [columnDefaultSortable=false](https://infinite-table.com/docs/reference/infinite-table-props.md#columnDefaultSortable) and then you can turn it back on per-column, by setting [column.defaultSortable=true](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.sortable).

### Multiple Sorting

If you want to use [multiple sorting](https://infinite-table.com/docs/learn/sorting/multiple-sorting.md), specify an array of objects like

```ts
// 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.

**Example: 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.

```ts
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(process.env.NEXT_PUBLIC_BASE_URL + `/developers100-sql?`)
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  firstName: { field: 'firstName' },

  country: { field: 'country' },
  salary: {
    field: 'salary',
    type: 'number',
  },
  age: { field: 'age' },
  id: { field: 'id' },
  canDesign: { field: 'canDesign' },
  preferredLanguage: { field: 'preferredLanguage' },
  stack: { field: 'stack' },

  hobby: { field: 'hobby' },
  city: { field: 'city' },
  currency: { field: 'currency' },
};

const domProps = { style: { height: '90vh' } };
const shouldReloadData = {
  sortInfo: false,
};
export default function LocalUncontrolledMultiSortingExampleWithRemoteData() {
  return (
    <>
      <DataSource<Developer>
        primaryKey="id"
        data={dataSource}
        defaultSortInfo={[
          { field: 'country', dir: -1 },
          { field: 'salary', dir: 1 },
        ]}
        shouldReloadData={shouldReloadData}
      >
        <InfiniteTable<Developer>
          debugId="local-uncontrolled-multi-sorting-example-with-remote-data"
          domProps={domProps}
          columns={columns}
          columnDefaultWidth={120}
        />
      </DataSource>
    </>
  );
}
```

**Example: Remote + uncontrolled multi-sorting example**

```ts
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(process.env.NEXT_PUBLIC_BASE_URL + `/developers100-sql?` + args)
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

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

const shouldReloadData = {
  sortInfo: true,
};
export default function RemoteUncontrolledMultiSortingExample() {
  return (
    <>
      <DataSource<Developer>
        primaryKey="id"
        data={dataSource}
        defaultSortInfo={[
          {
            field: 'salary',
            dir: -1,
          },
        ]}
        shouldReloadData={shouldReloadData}
      >
        <InfiniteTable<Developer>
          debugId="remote-uncontrolled-multi-sorting-example"
          columns={columns}
          columnDefaultWidth={120}
        />
      </DataSource>
    </>
  );
}
```

If you use uncontrolled sorting via [`defaultSortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#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`](https://infinite-table.com/docs/reference/datasource-props/index.md#sortInfo) prop.

## Understanding local and remote sorting

Sorting can be done both locally in the browser and remotely on the server. When you want sorting to be performed remotely on the server, a change on the [`sortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#sortInfo) should trigger a reload of the datasource. In order to achieve this, you need to specify [shouldReloadData.sortInfo=true](https://infinite-table.com/docs/reference/datasource-props/index.md#shouldReloadData.sortInfo).

Possible values for [`shouldReloadData.sortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#shouldReloadData.sortInfo) are `false` (sorting will be performed locally and won't trigger a reload of the [`data`](https://infinite-table.com/docs/reference/datasource-props/index.md#data) source) and `true` (sorting will be performed remotely and will trigger a reload of the data).

This allows you fine-grained control on how sorting is done, either in the client or on the server.

### Uncontrolled sorting

If you use uncontrolled sorting (namely you don't care about updating the [`sortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#sortInfo) yourself as a result of user interaction - via [`onSortInfoChange`](https://infinite-table.com/docs/reference/datasource-props/index.md#onSortInfoChange)) - then by default, the [`shouldReloadData.sortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#shouldReloadData.sortInfo) is `false` unless you specify otherwise.

You can initially render the component with no sort state or you can specify a default sorting state, via the uncontrolled prop [`defaultSortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#defaultSortInfo).

```tsx
// initially render the component with ascending sorting on `firstName` field
// also, note this is an array, so multiple sorting will be enabled
const defaultSortInfo = [{ field: 'firstName', dir: 1 }];

<DataSource<Developer>
  primaryKey="id"
  data={data}
  defaultSortInfo={defaultSortInfo}
>
  <InfiniteTable />
</DataSource>;
```

If your data is remote and you want the sorting to happen on the backend, you can still use uncontrolled sorting, but you need to specify [shouldReloadData.sortInfo=true](https://infinite-table.com/docs/reference/datasource-props/index.md#shouldReloadData.sortInfo).

Using remote sort mode will trigger a call to the [`data`](https://infinite-table.com/docs/reference/datasource-props/index.md#data) function whenever sorting changes, so you can re-fetch the data from the backend, according to the new `sortInfo`.

Whe `local` uncontrolled sorting is used, the `<DataSource />` sorts the data internally, based on the existing sorting information. To start with a specific `sortInfo`, use the [`defaultSortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#defaultSortInfo) prop. As the user interacts with the table, [`onSortInfoChange`](https://infinite-table.com/docs/reference/datasource-props/index.md#onSortInfoChange) is being called with updated sort info and the `<DataSource />` continues to sort the data accordingly.

The [`defaultSortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#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](https://infinite-table.com/docs/reference/datasource-props/index.md#sortInfo) prop - read the next section for more details

### Controlled Sorting

When you use the controlled [`sortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#sortInfo) prop, by default the [`shouldReloadData.sortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#shouldReloadData.sortInfo) is set to `true`, unless you specify otherwise.

Also, be aware that when the user interacts with the DataGrid when controlled sorting is configured, the [`sortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#sortInfo) prop will not update automatically - you need to listen to [`onSortInfoChange`](https://infinite-table.com/docs/reference/datasource-props/index.md#onSortInfoChange) and update the [`sortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#sortInfo) yourself.

Just like with uncontrolled sorting, updating the controlled [`sortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#sortInfo) when `shouldReloadData.sortInfo` is `true`, will trigger a call to the [`data`](https://infinite-table.com/docs/reference/datasource-props/index.md#data) function, so new sorted data can be re-fetched.

When the controlled [`sortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#sortInfo) is combined with [shouldReloadData.sortInfo=false](https://infinite-table.com/docs/reference/datasource-props/index.md#shouldReloadData.sortInfo), the `<DataSource />` will sort the data internally, on any changes of the sorting information.

But remember it's your responsibility to update the [`sortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#sortInfo) prop when the user interacts with the DataGrid.

Both controlled [`sortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#sortInfo) and uncontrolled [`defaultSortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#defaultSortInfo) work in combination with [`onSortInfoChange`](https://infinite-table.com/docs/reference/datasource-props/index.md#onSortInfoChange) - use it to be notified when sorting changes, so you can react and update your app accordingly if needed.

### Local Sorting

When you use uncontrolled sorting locally, the `<DataSource />` will sort the data internally, based on the [`defaultSortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#defaultSortInfo) prop. Local sorting is available for any configured [`data`](https://infinite-table.com/docs/reference/datasource-props/index.md#data) source - be it an array or a function that returns a promise.

You can use [`onDataParamsChange`](https://infinite-table.com/docs/reference/datasource-props/index.md#onDataParamsChange), which is called whenever any of the sorting, filtering, grouping or pivoting information changes.

**Example: Local uncontrolled sorting + local data**

```ts
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(process.env.NEXT_PUBLIC_BASE_URL + `/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' },
};
const shouldReloadData = {
  sortInfo: false,
};
export default function LocalUncontrolledSingleSortingExampleWithRemoteData() {
  return (
    <>
      <DataSource<Developer>
        primaryKey="id"
        data={dataSource}
        defaultSortInfo={{ field: 'salary', dir: -1 }}
        shouldReloadData={shouldReloadData}
      >
        <InfiniteTable<Developer>
          debugId="local-uncontrolled-single-sorting-example-with-remote-data"
          columns={columns}
          columnDefaultWidth={220}
        />
      </DataSource>
    </>
  );
}
```

**Example: Local uncontrolled sorting + remote data**

```ts
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(process.env.NEXT_PUBLIC_BASE_URL + `/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' },
};
const shouldReloadData = {
  sortInfo: false,
};
export default function LocalUncontrolledSingleSortingExampleWithRemoteData() {
  return (
    <>
      <DataSource<Developer>
        primaryKey="id"
        data={dataSource}
        defaultSortInfo={{ field: 'salary', dir: -1 }}
        shouldReloadData={shouldReloadData}
      >
        <InfiniteTable<Developer>
          debugId="local-uncontrolled-single-sorting-example-with-remote-data"
          columns={columns}
          columnDefaultWidth={220}
        />
      </DataSource>
    </>
  );
}
```

### Remote Sorting

Sorting remotely makes a lot of sense when using a function as your [`data`](https://infinite-table.com/docs/reference/datasource-props/index.md#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.

For remote sorting, make sure you specify [shouldReloadData.sortInfo=true](https://infinite-table.com/docs/reference/datasource-props/index.md#shouldReloadData.sortInfo) - 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).

**Example: Remote + controlled multi-sorting example**

```ts
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(process.env.NEXT_PUBLIC_BASE_URL + `/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,
    },
  ]);

  const shouldReloadData = {
    sortInfo: true,
  };
  return (
    <>
      <DataSource<Developer>
        primaryKey="id"
        data={dataSource}
        sortInfo={sortInfo}
        shouldReloadData={shouldReloadData}
        onSortInfoChange={setSortInfo}
      >
        <InfiniteTable<Developer>
          debugId="remote-controlled-multi-sorting-example"
          columns={columns}
          columnDefaultWidth={220}
        />
      </DataSource>
    </>
  );
}
```

In the example above, remote and controlled sorting are combined - because `shouldReloadData.sortInfo=true` 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](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.dataType), or, [a column `sortType`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.sortType).

There are 3 `dataType` values that can be used:

- `"string"`
- `"number"`
- `"date"`

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

Sorting works in combination with the [`sortTypes`](https://infinite-table.com/docs/reference/infinite-table-props.md#sortTypes) property, which is an object with keys being sort types and values being functions that compare two values of the same type.

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

Those are the three sort types supported by default.

The functions specified in the [`sortTypes`](https://infinite-table.com/docs/reference/infinite-table-props.md#sortTypes) object need to always sort data in ascending order.

A column can choose to use a specific [`columns.sortType`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.sortType), in which case, for local sorting, the corresponding sort function will be used, or, it can simply specify a [dataType](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.dataType) and the `sortType` with the same name will be used (when no explicit [sortType](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.sortType) is defined).

To conclude, the [dataType](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.dataType) of a column will be used as the [sortType](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.sortType) and [filterType](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.filterType), when those are not explicitly specified.

**Example: Custom sort by color - magenta will come first**

```ts
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> debugId="sortTypes-example" columns={columns} />
      </DataSource>
    </>
  );
}
```

In this example, for the `"color"` column, we specified [column.sortType="color"](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.sortType) - 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 used [column.sortType](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.sortType) to only change how the data is sorted.

When you provide a [`defaultSortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#defaultSortInfo) prop and the sorting information uses a custom [sortType](https://infinite-table.com/docs/reference/datasource-props/index.md#sortTypes), make sure you specify that as the `type` property of the sorting info object.

```tsx
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`](https://infinite-table.com/docs/reference/datasource-props/index.md#sortTypes) object as well.

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

## Replacing the sort function

While there are many ways to customise sorting, including the [`sortTypes`](https://infinite-table.com/docs/reference/datasource-props/index.md#sortTypes) mentioned above, you might want to completely replace the sorting function used by the `<DataSource />` component.

You can do this by configuring the [`sortFunction`](https://infinite-table.com/docs/reference/datasource-props/index.md#sortFunction) prop.

```tsx
const sortFunction = (sortInfo, dataArray) => {
  // sort the dataArray according to the sortInfo
  // and return the sorted array
  // return sortedDataArray;
};
<DataSource<T> sortFunction={sortFunction} />;
```

The function specified in the [`sortFunction`](https://infinite-table.com/docs/reference/datasource-props/index.md#sortFunction) prop is called with the [`sortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#sortInfo) as the first argument and the data array as the second. It should return a sorted array, as per the [`sortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#sortInfo) it was called with.

When [`sortFunction`](https://infinite-table.com/docs/reference/datasource-props/index.md#sortFunction) is specified, [`shouldReloadData.sortInfo`](https://infinite-table.com/docs/reference/datasource-props/index.md#shouldReloadData.sortInfo) will be forced to `false`, as the sorting is done in the browser.

**Example: Using a custom sortFunction**

```ts
import {
  InfiniteTable,
  DataSource,
  DataSourceSingleSortInfo,
  multisort,
} 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' },
};

const defaultSortInfo: DataSourceSingleSortInfo<Developer> = {
  field: 'stack',
  dir: 1,
};

const sortFunction = (
  sortInfo: DataSourceSingleSortInfo<Developer>[],
  arr: Developer[],
) => {
  // you call the default sorting
  const result = multisort<Developer>(sortInfo, arr);

  // and also apply your custom sorting
  // result.sort((a, b) => {
  // })

  return result;
};
export default function LocalUncontrolledSingleSortingExample() {
  return (
    <>
      <DataSource<Developer>
        primaryKey="id"
        data={dataSource}
        defaultSortInfo={defaultSortInfo}
        sortFunction={sortFunction}
      >
        <InfiniteTable<Developer>
          debugId="local-sortFunction-single-sorting-example-with-local-data-example"
          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: 9,
    firstName: 'Lurline',
    lastName: 'Deckow',
    country: 'Canada',
    city: 'Raymore',
    age: 31,
    currency: 'EUR',
    preferredLanguage: 'Rust',
    stack: 'frontend',
    canDesign: 'yes',
    salary: 58000,
    hobby: 'sports',
    email: 'Lurline_Deckow@gmail.com',
  },
];
```
