> ## Documentation Index
> Fetch the complete documentation index at: https://algolia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# sortBy

> Shows a list of indices for alternative sorting or ranking of search results.

export const Index = () => <Tooltip tip="An Algolia index is a searchable dataset that consists of records and configuration settings. These settings define how the records are searched and ranked.">
    index
  </Tooltip>;

```ts Signature theme={"system"}
sortBy({
  container: string | HTMLElement,
  items: object[],
  // Optional parameters
  cssClasses?: object,
  transformItems?: function,
});
```

<CodeGroup>
  ```js Package manager theme={"system"}
  import { sortBy } from "instantsearch.js/es/widgets";
  ```

  ```js CDN theme={"system"}
  const { sortBy } = instantsearch.widgets;
  // or directly use instantsearch.widgets.sortBy()
  ```
</CodeGroup>

<Card title="See this widget in action" icon="monitor-play" href="https://instantsearchjs.netlify.app/stories/js/?path=/story/sorting-sortby--default" horizontal>
  Preview this widget and its behavior.
</Card>

## About this widget

This widget displays a list of indices,
allowing a user to change the way hits are sorting
(with [replica indices](/doc/guides/managing-results/refine-results/sorting/how-to/creating-replicas)).
Another common use case for this widget is to let users switch between different indices.

For this widget to work,
you must define all indices that you pass down as options as replicas of the main <Index />.

## Examples

```js JavaScript icon=code theme={"system"}
sortBy({
  container: "#sort-by",
  items: [
    { label: "Featured", value: "instant_search" },
    { label: "Price (asc)", value: "instant_search_price_asc" },
    { label: "Price (desc)", value: "instant_search_price_desc" },
  ],
});
```

## Options

<ParamField body="container" type="string | HTMLElement" required>
  The CSS Selector or `HTMLElement` to insert the widget into.

  <CodeGroup>
    ```js string theme={"system"}
    sortBy({
      // ...
      container: "#sort-by",
    });
    ```

    ```js HTMLElement theme={"system"}
    sortBy({
      // ...
      container: document.querySelector("#sort-by"),
    });
    ```
  </CodeGroup>
</ParamField>

<ParamField body="items" type="object[]" required>
  The list of indices to search in, with each item:

  * `label: string`. The label of the index to display.
  * `value: string`. The name of the index to target.

  ```js JavaScript icon=code theme={"system"}
  sortBy({
    // ...
    items: [
      { label: "Featured", value: "instant_search" },
      { label: "Price (asc)", value: "instant_search_price_asc" },
      { label: "Price (desc)", value: "instant_search_price_desc" },
    ],
  });
  ```
</ParamField>

<ParamField body="cssClasses" type="object" default="{}">
  The [CSS classes you can override](/doc/guides/building-search-ui/widgets/customize-an-existing-widget/js#style-your-widgets):

  * `root`. The root element of the widget.
  * `select`. The`select` element.
  * `option`. The `option` elements of the `select`.

  ```js JavaScript icon=code theme={"system"}
  sortBy({
    // ...
    cssClasses: {
      root: "MyCustomSortBy",
      select: ["MyCustomSortBySelect", "MyCustomSortBySelect--subclass"],
    },
  });
  ```
</ParamField>

<ParamField body="transformItems" type="function" default="items => items">
  A function that receives the list of items before they are displayed.
  It should return a new array with the same structure.
  Use this to transform, filter, or reorder the items.

  The function also has access to the full `results` data,
  including all standard [response parameters](/doc/guides/building-search-ui/going-further/backend-search/in-depth/understanding-the-api-response/)
  and [parameters from the helper](https://community.algolia.com/algoliasearch-helper-js/reference.html#query-parameters),
  such as `disjunctiveFacetsRefinements`.

  ```js JavaScript icon=code theme={"system"}
  sortBy({
    // ...
    transformItems(items) {
      return items.map((item) => ({
        ...item,
        label: item.label.toUpperCase(),
      }));
    },
  });

  // or, combined with results
  sortBy({
    // ...
    transformItems(items, { results }) {
      return results?.nbPages < 4
        ? items.filter((item) => item.value === "instant_search")
        : items;
    },
  });
  ```
</ParamField>

## HTML output

```html HTML icon=code-xml theme={"system"}
<div class="ais-SortBy">
  <select class="ais-SortBy-select">
    <option class="ais-SortBy-option" value="instant_search">Featured</option>
    <option class="ais-SortBy-option" value="instant_search_price_asc">
      Price asc.
    </option>
    <option class="ais-SortBy-option" value="instant_search_price_desc">
      Price desc.
    </option>
  </select>
</div>
```

## Customize the UI with `connectSortBy`

If you want to create your own UI of the `sortBy` widget, you can use connectors.

To use `connectSortBy`, you can import it with the declaration relevant to how you installed InstantSearch.js.

<CodeGroup>
  ```js Package manager theme={"system"}
  import { connectSortBy } from "instantsearch.js/es/connectors";
  ```

  ```js CDN theme={"system"}
  const { connectSortBy } = instantsearch.connectors;
  // or directly use instantsearch.connectors.connectSortBy()
  ```
</CodeGroup>

Then it's a 3-step process:

```jsx JavaScript icon=code theme={"system"}
// 1. Create a render function
const renderSortBy = (renderOptions, isFirstRender) => {
  // Rendering logic
};

// 2. Create the custom widget
const customSortBy = connectSortBy(renderSortBy);

// 3. Instantiate
search.addWidgets([
  customSortBy({
    // instance params
  }),
]);
```

### Create a render function

This rendering function is called before the first search (`init` lifecycle step)
and each time results come back from Algolia (`render` lifecycle step).

```js JavaScript icon=code theme={"system"}
const renderSortBy = (renderOptions, isFirstRender) => {
  const { options, currentRefinement, canRefine, refine, widgetParams } =
    renderOptions;

  if (isFirstRender) {
    // Do some initial rendering and bind events
  }

  // Render the widget
};
```

#### Rendering options

<ParamField body="options" type="object[]">
  The list of items the widget can display, with each item:

  * `label: string`. The label of the index to display.
  * `value: string`. The name of the index to target.

  ```js JavaScript icon=code theme={"system"}
  const renderSortBy = (renderOptions, isFirstRender) => {
    const { options } = renderOptions;

    document.querySelector("#sort-by").innerHTML = `
      <select>
        ${options
          .map(
            (option) => `
              <option value="${option.value}">
                ${option.label}
              </option>
            `,
          )
          .join("")}
      </select>
    `;
  };
  ```
</ParamField>

<ParamField body="currentRefinement" type="string">
  The currently selected index.

  ```js JavaScript icon=code theme={"system"}
  const renderSortBy = (renderOptions, isFirstRender) => {
    const { options, currentRefinement } = renderOptions;

    document.querySelector("#sort-by").innerHTML = `
      <select>
        ${options
          .map(
            (option) => `
              <option
                value="${option.value}"
                ${option.value === currentRefinement ? "selected" : ""}
              >
                ${option.label}
              </option>
            `,
          )
          .join("")}
      </select>
    `;
  };
  ```
</ParamField>

<ParamField body="canRefine" type="boolean" post={['since: v4.45.0']}>
  Whether the search can be refined.

  ```js JavaScript icon=code theme={"system"}
  const renderSortBy = (renderOptions, isFirstRender) => {
    // `canRefine` is only available from v4.45.0
    // Use `hasNoResults` in earlier minor versions.
    const { options, currentRefinement, canRefine } = renderOptions;

    document.querySelector("#sort-by").innerHTML = `
      <select ${!canRefine ? "disabled" : ""}>
        ${options
          .map(
            (option) => `
              <option
                value="${option.value}"
                ${option.value === currentRefinement ? "selected" : ""}
              >
                ${option.label}
              </option>
            `,
          )
          .join("")}
      </select>
    `;
  };
  ```
</ParamField>

<ParamField body="refine" type="function">
  Switches indices and triggers a new search.

  ```js JavaScript icon=code theme={"system"}
  const renderSortBy = (renderOptions, isFirstRender) => {
    const { options, currentRefinement, refine } = renderOptions;

    const container = document.querySelector("#sort-by");

    if (isFirstRender) {
      const select = document.createElement("select");

      select.addEventListener("change", (event) => {
        refine(event.target.value);
      });

      container.appendChild(select);
    }

    container.querySelector("select").innerHTML = `
      ${options
        .map(
          (option) => `
            <option
              value="${option.value}"
              ${option.value === currentRefinement ? "selected" : ""}
            >
              ${option.label}
            </option>
          `,
        )
        .join("")}
    `;
  };
  ```
</ParamField>

<ParamField body="widgetParams" type="object">
  All original widget options forwarded to the render function.

  ```js JavaScript icon=code theme={"system"}
  const renderSortBy = (renderOptions, isFirstRender) => {
    const { widgetParams } = renderOptions;

    widgetParams.container.innerHTML = "...";
  };

  // ...

  search.addWidgets([
    customSortBy({
      container: document.querySelector("#sort-by"),
    }),
  ]);
  ```
</ParamField>

### Create and instantiate the custom widget

First, create your custom widgets using a rendering function.
Then, instantiate them with parameters.

There are two kinds of parameters you can pass:

* **Instance parameters**. Predefined options that configure Algolia's behavior.
* **Custom parameters**. Parameters you define to make the widget reusable and adaptable.

Inside the `renderFunction`, both instance and custom parameters are accessible through `connector.widgetParams`.

```js JavaScript icon=code theme={"system"}
const customSortBy = connectSortBy(
  renderSortBy
);

search.addWidgets([
  customSortBy({
    items: object[],
    // Optional parameters
    transformItems: function,
  })
]);
```

#### Instance options

<ParamField body="items" type="object[]" required>
  The list of indices to search in, with each item:

  * `label: string`. The label of the index to display.
  * `value: string`. The name of the index to target.

  ```js JavaScript icon=code theme={"system"}
  customSortBy({
    items: [
      { label: "Featured", value: "instant_search" },
      { label: "Price (asc)", value: "instant_search_price_asc" },
      { label: "Price (desc)", value: "instant_search_price_desc" },
    ],
  });
  ```
</ParamField>

<ParamField body="transformItems" type="function" default="items => items">
  A function that receives the list of items before they are displayed.
  It should return a new array with the same structure.
  Use this to transform, filter, or reorder the items.

  The function also has access to the full `results` data,
  including all standard [response parameters](/doc/guides/building-search-ui/going-further/backend-search/in-depth/understanding-the-api-response/)
  and [parameters from the helper](https://community.algolia.com/algoliasearch-helper-js/reference.html#query-parameters),
  such as `disjunctiveFacetsRefinements`.

  ```js JavaScript icon=code theme={"system"}
  customSortBy({
    transformItems(items) {
      return items.map((item) => ({
        ...item,
        label: item.label.toUpperCase(),
      }));
    },
  });

  // or, combined with results
  customSortBy({
    // ...
    transformItems(items, { results }) {
      return results?.nbPages < 4
        ? items.filter((item) => item.value === "instant_search")
        : items;
    },
  });
  ```
</ParamField>

### Full example

<CodeGroup>
  ```html HTML theme={"system"}
  <div id="sort-by"></div>
  ```

  ```js JavaScript theme={"system"}
  // Create the render function
  const renderSortBy = (renderOptions, isFirstRender) => {
    const {
      options,
      currentRefinement,
      refine,
      widgetParams,
      // `canRefine` is only available from v4.45.0
      // Use `hasNoResults` in earlier minor versions.
      canRefine,
    } = renderOptions;

    if (isFirstRender) {
      const select = document.createElement("select");

      select.addEventListener("change", (event) => {
        refine(event.target.value);
      });

      widgetParams.container.appendChild(select);
    }

    const select = widgetParams.container.querySelector("select");

    select.disabled = !canRefine;

    select.innerHTML = `
      ${options
        .map(
          (option) => `
            <option
              value="${option.value}"
              ${option.value === currentRefinement ? "selected" : ""}
            >
              ${option.label}
            </option>
          `,
        )
        .join("")}
    `;
  };

  // Create the custom widget
  const customSortBy = connectSortBy(renderSortBy);

  // Instantiate the custom widget
  search.addWidgets([
    customSortBy({
      container: document.querySelector("#sort-by"),
      items: [
        { label: "Featured", value: "instant_search" },
        { label: "Price (asc)", value: "instant_search_price_asc" },
        { label: "Price (desc)", value: "instant_search_price_desc" },
      ],
    }),
  ]);
  ```
</CodeGroup>
