# Sortable Columns

**Category:** Features/Sort/Sortable Columns/Sortable Columns

## Design

### Description

The sortable columns feature allows users to sort a table by clicking on a column header, sorting the table based on the values in that column.

> **Note:** To allow visitors to sort by multiple columns, you need to enable [Multi Level Sorting](./?path=/story/features-sort-sortable-columns-multilevelsorting--multilevelsorting).

#### Usage

1. Define a [`Table`](./?path=/story/base-components-collections-table-table--table) collection component.

1. Define the table columns by passing [`TableColumn`](./?path=/story/common-types--tablecolumn) objects to the `columns` prop on the `Table`, specifiying the following properties:

    * `id`: A unique identifier for the column. **Required** to enable sortable columns.
    * `sortable`: Set to `true` to make the column sortable. **Required** to enable sortable columns.
    * `defaultSortOrder`: Determines the default sort order for the column, either `asc` or `desc`. **Optional**.
    * `sortMode`: Defines the order in which sorting cycles through when the column header is clicked. The default cycle is `asc` `->` `desc` `->` `undefined` (no sort) `->` `asc`. Customize by passing an array. For example, `[undefined, 'desc', 'asc']`. **Optional.**

#### Backend integration

The [`query`](./?path=/story/common-types--computedquery) argument passed to [`fetchData`](./?path=/story/common-hooks--usecollection) includes a `sort` option. For tables, only one column can be sorted at a time, so the array will either be empty or contain a single sort object. Given your server supports sorting, convert the `sort` array to the appropriate parameter format for your server.


### Basic sortable column

The **Name** column is defined as sortable in the relevant object in the `columns` array.

```tsx
import { Avatar } from '@wix/design-system';
import React from 'react';
import { Edit } from '@wix/wix-ui-icons-common';
import { CollectionPage } from '@wix/patterns/page';
import { Table, useTableCollection, OffsetQuery } from '@wix/patterns';
import { contacts } from '@wix/crm';

function SortableColumnsDefaultSorting() {

  const state = useTableCollection<contacts.Contact>({
    queryName: 'contacts-SortableColumnsDefaultSorting',
    fetchData: (query: OffsetQuery) => {
      const { limit, offset, search, sort, filters } = query;

      let queryBuilder = contacts.queryContacts().limit(limit).skip(offset);

      if (search) {
        queryBuilder = queryBuilder.startsWith('info.name.first', search);
      }

      if (sort) {
        for (const sortLevel of sort) {
          queryBuilder =
            sortLevel.order === 'asc'
              ? queryBuilder.ascending(
                  sortLevel.fieldName as 'info.name.first' | 'info.jobTitle',
                )
              : queryBuilder.descending(
                  sortLevel.fieldName as 'info.name.first' | 'info.jobTitle',
                );
        }
      }

      return queryBuilder.find().then(({ items = [], totalCount: total }) => ({
        items,
        total,
      }));
    },
    itemName: (item) => `${item.info?.name?.first} ${item.info?.name?.last}`,
    fetchErrorMessage: () => 'Error fetching contacts',
  });

  return (
    <CollectionPage height="400px">
      <CollectionPage.Header title={{ text: 'Contacts' }} />
      <CollectionPage.Content>
        <Table
          state={state}
          columns={[
            {
              title: '',
              width: '50px',
              render: (contact) => (
                <Avatar
                  name={`${contact.info?.name?.first} ${contact.info?.name?.last}`}
                  imgProps={{ src: contact.info?.picture?.image }}
                />
              ),
            },
            {
              id: 'info.name.first',
              title: 'Name',
              width: '250px',
              sortable: true,
              defaultSortOrder: 'asc',
              render: (contact) =>
                `${contact.info?.name?.first} ${contact.info?.name?.last}`,
            },
            {
              title: 'Last Activity',
              render: (contact) =>
                contact.lastActivity?.activityDate?.toLocaleString(),
            },
          ]}
          actionCell={(contact, index) => ({
            secondaryActions: [
              {
                icon: <Edit />,
                text: 'Edit',
                disabled:
                  index % 5 === 0 || contact.info?.name?.first?.startsWith('A'),
                onClick: () => {},
              },
            ],
          })}
        />
      </CollectionPage.Content>
    </CollectionPage>
  );
}
```

### Custom sort order

Use the `sortMode` property in each columns object in the `columns` array to define the order of the sort options for that column.

```tsx
import { Avatar } from '@wix/design-system';
import React from 'react';
import { Edit } from '@wix/wix-ui-icons-common';
import { CollectionPage } from '@wix/patterns/page';
import { Table, useTableCollection, OffsetQuery } from '@wix/patterns';
import { contacts } from '@wix/crm';

function SortMode() {
  const state = useTableCollection<contacts.Contact>({
    queryName: 'contacts-SortMode',
    fetchData: (query: OffsetQuery) => {
      const { limit, offset, search, sort } = query;

      let queryBuilder = contacts.queryContacts().limit(limit).skip(offset);

      if (search) {
        queryBuilder = queryBuilder.startsWith('info.name.first', search);
      }

      if (sort) {
        for (const sortLevel of sort) {
          queryBuilder =
            sortLevel.order === 'asc'
              ? queryBuilder.ascending(
                  sortLevel.fieldName as 'info.name.first' | 'info.jobTitle',
                )
              : queryBuilder.descending(
                  sortLevel.fieldName as 'info.name.first' | 'info.jobTitle',
                );
        }
      }

      return queryBuilder.find().then(({ items = [], totalCount: total }) => ({
        items,
        total,
      }));
    },
    itemName: (item) => `${item.info?.name?.first} ${item.info?.name?.last}`,
    fetchErrorMessage: () => 'Error fetching contacts',
  });

  return (
    <CollectionPage height="400px">
      <CollectionPage.Header title={{ text: 'Contacts' }} />
      <CollectionPage.Content>
        <Table
          state={state}
          columns={[
            {
              title: '',
              width: '50px',
              render: (contact) => (
                <Avatar
                  name={`${contact.info?.name?.first} ${contact.info?.name?.last}`}
                  imgProps={{ src: contact.info?.picture?.image }}
                />
              ),
            },
            {
              id: 'info.name.first',
              title: 'Name',
              width: '250px',
              sortable: true,
              defaultSortOrder: 'asc',
              render: (contact) =>
                `${contact.info?.name?.first} ${contact.info?.name?.last}`,
              sortMode: ['asc', 'desc'],
            },
            {
              id: 'lastActivity',
              title: 'Last Activity',
              render: (contact) =>
                contact.lastActivity?.activityDate?.toLocaleString(),
              defaultSortOrder: 'asc',
              sortable: true,
              sortMode: ['asc', undefined, 'desc'],
            },
          ]}
          actionCell={(contact, index) => ({
            secondaryActions: [
              {
                icon: <Edit />,
                text: 'Edit',
                disabled:
                  index % 5 === 0 || contact.info?.name?.first?.startsWith('A'),
                onClick: () => {},
              },
            ],
          })}
        />
      </CollectionPage.Content>
    </CollectionPage>
  );
}
```

## BI

### Events

 Event   |   Description   |
--------- | --------------- |
[144:120](https://bo.wix.com/data-tools/bi-catalog-app/event/144:120) | Sent when a user sorts a WixPatterns component
[144:147](https://bo.wix.com/data-tools/bi-catalog-app/event/144:147) |  Sent when a user changes the order of the columns that sort a WixPatterns component




### Component load events
Event   |   Description   |
--------- | --------------- |
[144:110](https://bo.wix.com/data-tools/bi-catalog-app/event/144:110) | Sent when a Wix Patterns component starts loading
[144:111](https://bo.wix.com/data-tools/bi-catalog-app/event/144:111) | Sent when a Wix Patterns component is done loading


#### 🐪 Couldn't find what you need?
* Check our [BI Catalog](https://bo.wix.com/data-tools/bi-catalog-app?viewId=all-items-view&selectedColumns=src%2Cevid%2Cname%2Cowner%2Cproduct%2CuserType+false%2CdateUpdated%2CdateCreated+false%2CcreatedBy+false%2Cstatus&source=%5B%7B%22id%22%3A%22144%22%2C%22name%22%3A%22144+-+Cairo%22%7D%5D)
* Contact us on [#cairo-bi](https://wix.slack.com/archives/C03N53KURH9)


