﻿---
description: Компонент для выбора значения из выпадающего списка.
tags: selection
---

<Overview group="forms">

# CustomSelect [tag:component]

Компонент для выбора значения из выпадающего списка.
Поддерживает широкие возможности по изменению внешнего вида, поиск по опциям и пользовательский рендеринг опций.

Связанные компоненты:

- [`Select`](/components/select)

</Overview>

> Важно: для отображения невыбранного состояния нужно использовать `value=null` вместо `undefined`.
>
> `undefined` ипользуется только для неконтролируемого компонента.

{/* @example-description: Базовый `CustomSelect` с набором опций и кнопкой очистки выбора. */}
<Playground style={{ maxWidth: 270 }}>

```jsx
<CustomSelect
  options={[
    { value: 0, label: 'Москва' },
    { value: 1, label: 'Санкт-Петербург' },
    { value: 2, label: 'Новосибирск' },
    { value: 3, label: 'Йошкар-Ола' },
  ]}
  placeholder="Выберите город"
  allowClearButton
/>
```

</Playground>

## Режим работы

Компонент поддерживает работу как в неконтролируемом режиме, так и контролируемом. Это стандартное поведение
React-компонентов, прочитать про это можно в [документации React](https://react.dev/reference/react-dom/components/input#controlling-an-input-with-a-state-variable).

Для использования неконтролируемого режима достаточно просто _не_ передавать `value` или передавать `defaultValue`, если
требуется задать значение по умолчанию.
Для контролируемого режима используйте связку свойств `value`/`onChange` для задания значения и его изменения.

```jsx
const colors = [
  {
    value: 'red',
    label: 'Красный',
  },
  {
    value: 'green',
    label: 'Зелёный',
  },
  {
    value: 'blue',
    label: 'Синий',
  },
];

// Неконтролируемое состояние
<CustomSelect options={colors} defaultValue="red" />;

// Контролируемое состояние
const [color, setColor] = React.useState('red');

<CustomSelect options={colors} value={color} onChange={(_, newColor) => setColor(newColor)} />;
```

## Состояния

### `disabled`

Свойство `disabled` блокирует взаимодействие с компонентом и добавляет визуальную индикацию недоступности.

{/* @example-description: `CustomSelect` в неактивном состоянии с предвыбранным значением. */}
<Playground style={{ maxWidth: 270 }}>

```jsx
<CustomSelect options={[{ value: 'red', label: 'Красный' }]} defaultValue="red" disabled />
```

</Playground>

## Валидация

Свойство `status` используется для визуализации валидации компонента - некорректности заполненного поля (значение `"error"`)
или успешной валидации (значение `"valid"`).

> Если вы используете `CustomSelect` вместе с [`FormItem`](/components/form-item), вам достаточно указать свойство `status` только у
> [`FormItem`](/components/form-item).

{/* @example-description: Варианты валидации `CustomSelect` со статусами `error` и `valid`. */}
<Playground style={{ maxWidth: 270 }}>

```jsx
<CustomSelect options={[{ value: 'red', label: 'Красный' }]} defaultValue="red" status="error" />
<CustomSelect
  options={[{ value: 'green', label: 'Зеленый' }]}
  defaultValue="green"
  status="valid"
/>
```

</Playground>

## Кастомизация

### Визуальное оформление

С помощью свойства `renderOption` можно влиять на отображение конкретного элемента выпадающего списка.
Вы можете изменить стандартный компонент [`CustomSelectOption`](#customselectoption) или использовать свой компонент.

{/* @example-description: Кастомный рендер опций `CustomSelect` с дополнительным описанием города. */}
<Playground style={{ maxWidth: 270 }}>

```jsx
<CustomSelect
  options={[
    { value: 0, label: 'Москва', country: 'Россия' },
    { value: 1, label: 'Санкт-Петербург', country: 'Россия' },
    { value: 2, label: 'Новосибирск', country: 'Россия' },
    { value: 3, label: 'Йошкар-Ола', country: 'Россия' },
  ]}
  placeholder="Выберите город"
  allowClearButton
  renderOption={({ option, ...restProps }) => (
    <CustomSelectOption {...restProps} description={option.country} />
  )}
/>
```

</Playground>

## Кастомное поведение при поиске

Пример показывает, как реализовать кастомное поведение поиска: при вводе сохраняется локальный `query`, при отсутствии совпадений в списке появляется опция «Добавить пользователя …»,
её выбор добавляет новый элемент в `options` — вместе с пользовательским рендером опций это даёт удобный UX для создания элементов на лету.

{/* @example-description: Кастомный поиск с возможностью добавления новой опции прямо из выпадающего списка. */}
<Playground style={{ maxWidth: 270 }}>

```jsx
const users = [
  {
    id: 58690866,
    first_name: 'Эльдар',
    last_name: 'Мухаметханов',
    screen_name: 'e.muhamethanov',
  },
  {
    id: 1771114,
    first_name: 'Ином',
    last_name: 'Мирджамолов',
    screen_name: 'inomdzhon',
  },
  {
    id: 16790455,
    first_name: 'Вика',
    last_name: 'Жижонкова',
    screen_name: 'BlackySoul',
  },
  {
    id: 117253521,
    first_name: 'Даниил',
    last_name: 'Суворов',
    screen_name: 'SevereCloud',
  },
  {
    id: 99539515,
    first_name: 'Никита',
    last_name: 'Денисов',
    screen_name: 'qurle',
  },
  {
    id: 296221155,
    first_name: 'Алексей',
    last_name: 'Зайцев',
    screen_name: 'Zaycevq',
  },
];

const getUsers = (usersArray) =>
  usersArray.map((user) => ({
    label: `${user.first_name} ${user.last_name}`,
    value: `${user.id}`,
    description: user.screen_name,
  }));

const [value, setValue] = React.useState('');
const [query, setQuery] = React.useState('');
const [newUsers, setNewUsers] = React.useState([...getUsers(users)]);

const customSearchOptions = () => {
  const options = [...newUsers];
  if (query.length > 0 && !options.find((user) => user.value === query || user.label === query)) {
    options.unshift({
      label: `Добавить пользователя ${query}`,
      value: '0',
    });
  }
  return options;
};

const onCustomSearchChange = (_, newValue) => {
  if (newValue === '0') {
    setNewUsers([...newUsers, { label: query, value: query }]);
    setValue(query);
  } else {
    setValue(newValue);
  }
  setQuery('');
};

const onCustomSearchInputChange = (e) => {
  setQuery(e.target.value);
};

return (
  <CustomSelect
    value={value}
    placeholder="Введите имя пользователя"
    searchable
    options={customSearchOptions()}
    onInputChange={onCustomSearchInputChange}
    renderOption={({ option, ...restProps }) => (
      <CustomSelectOption
        style={option.value === '0' ? { color: 'var(--vkui--color_text_accent)' } : {}}
        {...restProps}
      >
        {option.label}
      </CustomSelectOption>
    )}
    onChange={onCustomSearchChange}
  />
);
```

</Playground>

## Кастомный алгоритм поиска

Демонстрация кастомного алгоритма поиска иллюстрирует передачу `filterFn` для гибкого соответствия (по `label` и `description`),
поддержку `searchable` и кастомного рендера опций — полезно, когда нужно искать по нескольким полям или использовать нестандартные критерии.

{/* @example-description: Кастомный алгоритм фильтрации опций через `filterFn` по нескольким полям. */}
<Playground style={{ maxWidth: 270 }}>

```jsx
const cities = [
  {
    label: 'Санкт-Петербург',
    description: 'Россия',
    value: '0',
  },
  {
    label: 'Москва',
    description: 'Россия',
    value: '1',
  },
  {
    label: 'Новосибирск',
    description: 'Россия',
    disabled: true,
    value: '2',
  },
  {
    label: 'Нью-Йорк',
    description: 'США',
    value: '3',
  },
  {
    label: 'Чикаго',
    description: 'США',
    value: '4',
  },
];

const customSearchFilter = (value, option) =>
  option.label.toLowerCase().includes(value.toLowerCase()) ||
  option.description.toLowerCase().includes(value.toLowerCase());

return (
  <CustomSelect
    id="custom-search-algo-select-id"
    placeholder="Введите название города или страны"
    searchable
    filterFn={customSearchFilter}
    renderOption={({ option, ...restProps }) => (
      <CustomSelectOption {...restProps} description={option.description} />
    )}
    options={cities}
  />
);
```

</Playground>

## Асинхронная загрузка списка

Асинхронный пример показывает подгрузку опций с задержкой/дебаунсом, управление состояниями `fetching`/`remoteQuery`,
минимальную длину запроса, очистку таймаутов и возможность заменить содержимое выпадающего списка (например, подсказкой «введите хотя бы три символа») — стандартный шаблон для работы с удалёнными API.

{/* @example-description: Асинхронная подгрузка опций `CustomSelect` с поиском, дебаунсом и состоянием загрузки. */}
<Playground style={{ maxWidth: 600 }}>

```jsx
const users = [
  {
    id: 58690866,
    first_name: 'Эльдар',
    last_name: 'Мухаметханов',
    screen_name: 'e.muhamethanov',
  },
  {
    id: 1771114,
    first_name: 'Ином',
    last_name: 'Мирджамолов',
    screen_name: 'inomdzhon',
  },
  {
    id: 16790455,
    first_name: 'Вика',
    last_name: 'Жижонкова',
    screen_name: 'BlackySoul',
  },
  {
    id: 117253521,
    first_name: 'Даниил',
    last_name: 'Суворов',
    screen_name: 'SevereCloud',
  },
  {
    id: 99539515,
    first_name: 'Никита',
    last_name: 'Денисов',
    screen_name: 'qurle',
  },
  {
    id: 296221155,
    first_name: 'Алексей',
    last_name: 'Зайцев',
    screen_name: 'Zaycevq',
  },
];

const getUsers = (usersArray) =>
  usersArray.map((user) => ({
    label: `${user.first_name} ${user.last_name}`,
    value: `${user.id}`,
    description: user.screen_name,
  }));

const [searchable, setSearchable] = React.useState(false);
const [remoteQuery, setRemoteQuery] = React.useState('');
const [fetching, setFetching] = React.useState(false);
const [remoteUsers, setRemoteUsers] = React.useState([]);

let timeout;

const cleanFetchingTimeout = () => {
  if (timeout) {
    clearTimeout(timeout);
  }
};

const fetchRemoteUsers = () => {
  setFetching(true);
  timeout = setTimeout(() => {
    setRemoteUsers([...getUsers(users)]);
    setFetching(false);
    cleanFetchingTimeout();
  }, 1500);
};

const searchRemoteUsers = (e) => {
  const _remoteQuery = e.target.value;
  cleanFetchingTimeout();
  setRemoteQuery(_remoteQuery);

  if (_remoteQuery.length < 3) {
    setRemoteUsers([]);
    setFetching(false);
  } else {
    fetchRemoteUsers();
  }
};

const clearRemoteUsers = () => {
  setRemoteUsers([]);
  setRemoteQuery('');
  cleanFetchingTimeout();
};

const renderDropdown = ({ defaultDropdownContent }) => {
  if (remoteQuery.length < 3) {
    return (
      <Text style={{ padding: 12, color: 'var(--vkui--color_text_secondary)' }}>
        Нужно ввести хотя бы три символа
      </Text>
    );
  }
  return defaultDropdownContent;
};

React.useEffect(() => {
  return () => cleanFetchingTimeout();
}, []);

return (
  <FormLayoutGroup mode="horizontal" style={{ width: '100%' }}>
    <FormItem style={{ flexGrow: 1, flexShrink: 1 }}>
      <CustomSelect
        aria-label="Пользователь"
        options={remoteUsers}
        searchable={searchable}
        placeholder={searchable ? 'Введите имя пользователя' : 'Не выбран'}
        disabled={searchable && fetching}
        onInputChange={searchable ? searchRemoteUsers : undefined}
        onOpen={searchable ? undefined : remoteUsers.length === 0 && fetchRemoteUsers}
        onClose={() => {
          console.log('CLOSED [async CustomSelect]');
        }}
        fetching={fetching}
        renderDropdown={searchable && !fetching && renderDropdown}
      />
    </FormItem>

    <FormItem style={{ flexBasis: '200px', flexGrow: 0 }}>
      <Checkbox
        onChange={(e) => {
          setSearchable(e.target.checked);
          clearRemoteUsers();
        }}
      >
        Использовать поиск
      </Checkbox>
    </FormItem>
  </FormLayoutGroup>
);
```

</Playground>

## CustomSelectOption [#custom-select-option]

Универсальный компонент для отрисовки одного из значений в выпадающем списке.
Передавайте данный компонент в свойство `renderOption`, для кастомизации значений выпадающего списка.
Помимо `CustomSelect` используется и в `ChipsSelect`.

{/* @example-description: Базовый пример `CustomSelectOption` с иконками в `before` и `after`. */}
<Playground style={{ maxWidth: 270 }}>
  ```jsx
  <CustomSelectOption before={<Avatar initials="ИИ" size={20} aria-hidden />} after={<Icon16Pin />}>
    Иван Иванов
  </CustomSelectOption>
  ```
</Playground>

Для реализации многоуровневых `CustomSelectOption` используйте свойство `hierarchy`,
которое позволяет создать нужный отступ в зависимости от уровня вложенности.

{/* @example-description: Многоуровневые опции с отступами через свойство `hierarchy`. */}
<Playground style={{ maxWidth: 270 }}>
  ```jsx
  <CustomSelectOption>Диск</CustomSelectOption>
  <CustomSelectOption hierarchy={1}>Папка</CustomSelectOption>
  <CustomSelectOption hierarchy={2}>Файл 1</CustomSelectOption>
  <CustomSelectOption hierarchy={2} selected>
    Файл 2
  </CustomSelectOption>
  ```
</Playground>

## Тестирование (e2e) [#e2e]

Для поиска компонента и его элементов на странице cуществует ряд вспомогательных свойств:

По умолчанию все `data-*` атрибуты попадают на `<input>` компонента, это значит, что при передаче
`data-testid` или `data-test-id` идентификатор попадёт `<input>`.
Доступ к полю ввода может быть полезен для:

- получения текста, введённого пользователем при поиске опций, в режиме `searchable`;
- получения информации о наличии фокуса на компоненте;
- симуляции работы с компонентом с клавиатуры.

Используйте `labelTextTestId`:

- для симуляции работы с компонентом с помощью мыши, тач-устройства;
- для получения текста выбранной в данный момент опции.

Для взаимодействия с кнопкой очистки состояния компонента, которая появляется,
если `CustomSelect` имеет свойство `searchable` и пользователь выбрал опцию, используйте свойство `clearButtonTestId`.

`CustomSelect` внутри себя хранит невидимый `<select>`, для того, чтобы `CustomSelect` можно было использовать внутри формы.
Для получения доступа к `<select>` используйте свойство `nativeSelectTestId`. Полезно для доступа к значению `value`,
выбранной в данный момент опции.

Все перечисленные выше свойства устанавливают аттрибут `data-testid` у соответствующих элементов. Учитывайте это при построении селекторов.

## Кастомизация

Компонент поддерживает свойство `slotProps`, которое даёт возможность прокинуть свойство в некоторые внутренние элементы.
Это удобно для добавления кастомных классов, data-атрибутов, aria-атрибутов, обработчиков событий, доступов к элементам через `getRootRef` и других расширений, не влияя на внешний API компонента.

{/* @example-description: Настройка внутренних слотов `CustomSelect` через `slotProps` и ссылки на DOM-элементы. */}
<Playground style={{ maxWidth: 270 }}>
  ```jsx
  const inputRef = React.useRef();
  const selectRef = React.useRef();

  return (
    <CustomSelect
      options={[
        { value: 0, label: 'Москва' },
        { value: 1, label: 'Санкт-Петербург' },
        { value: 2, label: 'Новосибирск' },
        { value: 3, label: 'Йошкар-Ола' },
      ]}
      placeholder="Выберите город"
      className="my-root-class"
      data-testid="select-root"
      id="custom-select-input-id"
      slotProps={{
        root: {
          id: 'select-root-id',
        },
        input: {
          className: 'my-input-class',
          getRootRef: inputRef,
        },
        select: {
          className: 'my-select-class',
          'data-test-id': 'select',
          getRootRef: selectRef,
        }
      }}
    />
  )
  ```
</Playground>

## Доступность (a11y) [#a11y]

Следуйте рекомендациям [доступности компонента `Select`](/components/select#a11y).

### Индикация ассинхронной загрузки

Для того, чтобы уведомить пользователей скринридеров о том, что идет загрузка списка опций использутся специальные текстовые метки совместно со свойством `fetching`.
Текст этих меток можно переопределить с помощью свойств `fetchingInProgressLabel` и `fetchingCompletedLabel`. По умолчанию они соответственно: `"Список опций загружается..."` и `"Опций загружено: ${options.length}"`

### Проблема определения выбранной опции

В текущей реализации (`v7`) компонент `CustomSelect` имеет проблему с доступностью в некоторых скринридерах (например, `NVDA`): пользователь не может узнать, какой вариант выбран.
Это происходит из-за сброса значения в `input` при потере фокуса.

Такое поведение было добавлено для ускорения ввода новых значений, но оно негативно влияет на доступность.

#### Решение

Мы изменили логику работы компонента. Чтобы включить исправленное поведение, используйте флаг `accessible`. Он введён для сохранения обратной совместимости в минорных обновлениях.

#### Рекомендация

С `v8` этот флаг теперь включен по умолчанию. Не рекомендуется отключать этот флаг, так как с `v9` этот флаг будет удален.

## Свойства и методы [#api]

### CustomSelect

| Свойство | Тип | По умолчанию | Описание |
| --- | --- | --- | --- |
| `accessible` | `boolean` | `-` | **Deprecated**: Since 8.0.0. Будет удалено в 9.0.0.  Включает режим в котором выбранное значение `CustomSelect` читается скринридерами корректно. В данном режиме введенное в поле ввода значение не сбрасывается при потере фокуса. |
| `after` | `ReactNode` | `-` | Добавляет иконку справа.  Рекомендации:  - Используйте следующие размеры иконок `12` \| `16` \| `20` \| `24` \| `28`. - Используйте [IconButton](https://vkui.io/components/icon-button), если вам нужна иконка, реагируюущая на нажатие. |
| `afterAlign` | `FieldIconsAlign` | `-` | Вертикальное выравнивание иконки справа. |
| `align` | `AlignType` | `-` |  |
| `allowClearButton` | `boolean` | `-` | Если `true`, то справа будет отображаться кнопка для очистки значения. |
| `before` | `ReactNode` | `-` | Добавляет иконку слева.  Рекомендации:  - Используйте следующие размеры иконок `12` \| `16` \| `20` \| `24` \| `28`. - Используйте [IconButton](https://vkui.io/components/icon-button), если вам нужна иконка, реагирующая на нажатие. |
| `beforeAlign` | `FieldIconsAlign` | `-` | Вертикальное выравнивание иконки слева. |
| `ClearButton` | `ComponentType<CustomSelectClearButtonProps>` | `-` | Кастомная кнопка для очистки значения. Должна принимать обязательное свойство `onClick`. |
| `clearButtonTestId` | `string` | `-` | Передает атрибут `data-testid` для кнопки очистки. |
| `defaultValue` | `SelectValue` | `-` | См. `value`. |
| `dropdownAutoWidth` | `boolean` | `-` | Ширина раскрывающегося списка зависит от контента. |
| `dropdownOffsetDistance` | `number` | `-` | Отступ от выпадающего списка. |
| `emptyText` | `string` | `-` | Текст, который будет отображен, если приходит пустой `options`. |
| `fetching` | `boolean` | `false` | Если `true`, то в дропдауне вместо списка опций рисуется спиннер. При переданных `renderDropdown` и `fetching: true` "победит" `renderDropdown`. |
| `fetchingCompletedLabel` | `string \| ((optionsCount: number) => string)` | ``Загружено опций: ${options.length}`` | Текстовая метка для индикации завершения процесса загрузки данных для пользователей скринридерами. По умолчанию: `"Загружено опций: ${options.length}"`. |
| `fetchingInProgressLabel` | `string` | `Список опций загружается...` | Текстовая метка для индикации процесса загрузки данных для пользователей скринридерами. По умолчанию: `"Список опций загружается..."`. |
| `filterFn` | `false \| FilterFn<OptionInterfaceT>` | `-` | Функция для кастомной фильтрации. По умолчанию поиск производится по `option.label`. |
| `forceDropdownPortal` | `boolean` | `-` | Использовать Portal для рендеринга выпадающего списка. |
| `getRef` | `Ref<HTMLSelectElement>` | `-` | **Deprecated**: Since 7.9.0. Вместо этого используйте `slotProps={ select: { getRootRef: ... } }`. |
| `getRootRef` | `Ref<HTMLDivElement>` | `-` |  |
| `getSelectInputRef` | `Ref<HTMLInputElement>` | `-` | **Deprecated**: Since 7.9.0. Вместо этого используйте `slotProps={ input: { getRootRef: ... } }`.  Ref на внутрений компонент input. |
| `icon` | `ReactNode` | `-` | Иконка раскрывающегося списка. |
| `labelTextTestId` | `string` | `-` | Передает атрибут `data-testid` для элемента, внутри которого отображается текст выбранной опции `CustomSelect` или плейсхолдер. |
| `mode` | `"default" \| "plain"` | `-` | **Deprecated**: Будет удалено в 10.0.0, используйте `selectType`.  Режим отображения.  - `default` — показывает фон, обводку и, при наличии, текст-подсказку. - `plain` — показывает только текст-подсказку. |
| `multiline` | `boolean` | `-` | Флаг для включения многострочного режима. |
| `nativeSelectTestId` | `string` | `-` | **Deprecated**: Since 7.9.0. Вместо этого используйте `slotProps={ select: { 'data-testid': ... } }`.  Передает атрибут `data-testid` для нативного элемента `select`. |
| `noMaxHeight` | `boolean` | `-` | Отключает максимальную высоту по умолчанию. |
| `onChange` | `((e: ChangeEvent<HTMLSelectElement>, newValue: SelectValue) => void)` | `-` | Обработчик, срабатывающий при изменении выбранного значения. Вторым параметром прокидывается новое значение.  > ⚠️ Лучше использовать второй параметр при работе с компонентом. |
| `onClose` | `VoidFunction` | `-` | Обработчик закрытия выпадающего списка. |
| `onInputChange` | `((e: ChangeEvent<HTMLInputElement>) => void)` | `-` | Событие изменения текстового поля. |
| `onInputKeyDown` | `((e: KeyboardEvent<Element>, isOpen: boolean) => void)` | `-` | Обработчик события `keyDown` в поле ввода. |
| `onOpen` | `VoidFunction` | `-` | Обработчик открытия выпадающего списка. |
| `**options** \*` | `OptionInterfaceT[]` | `-` | Список опций в списке. |
| `overscrollBehavior` | `"none" \| "auto" \| "contain"` | `-` | Поведение overscroll, подробнее можно почитать в [документации](https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior). |
| `placeholder` | `string` | `-` | Текст-подсказка при отсутствии выбранного значения. |
| `popupDirection` | `PopupDirection` | `-` | Направление раскрытия выпадающего списка. |
| `renderDropdown` | `({ defaultDropdownContent, }: { defaultDropdownContent: ReactNode; }) => ReactNode` | `-` | Рендер-проп для кастомного рендера содержимого дропдауна. В `defaultDropdownContent` содержится список опций в виде скроллящегося блока. |
| `renderOption` | `(props: CustomSelectRenderOption<OptionInterfaceT>) => ReactNode` | `-` | Рендер-проп для кастомного рендера опции. В объекте аргумента приходят [свойства опции](https://vkui.io/components/custom-select#custom-select-option-api).  > ⚠️  Важно: свойство опции `disabled` должно выставляться только через проп `options`. > Запрещается выставлять `disabled` проп опциям в обход `options`, иначе `CustomSelect` не будет знать об актуальном состоянии опции. |
| `searchable` | `boolean` | `-` | Если `true`, то при нажатии на `CustomSelect` в нём появится текстовое поле для поиска по `options`. По умолчанию поиск производится по `option.label`. |
| `selectType` | `SelectType` | `-` | Тип селекта, влияющий на отображение. |
| `slotProps` | `({ root?: (Omit<HTMLAttributes<HTMLDivElement>, "children"> & HasDataAttribute & HasRootRef<HTMLDivElement>); select?: (NativeHTMLSelectProps & ... 1 more ... & HasDataAttribute) \| undefined; } & { ...; }) \| undefined` | `-` | Свойства, которые можно прокинуть внутрь компонента: - `root`: свойства для прокидывания в корень компонента; - `select`: свойства для прокидывания в нативный `select`; - `input`: свойства для прокидывания в нативный `input`. |
| `status` | `"default" \| "error" \| "valid"` | `-` | Статус отображения поля в форме. |
| `value` | `SelectValue` | `-` | Выбранное значение.  > ⚠️  Важно: При прокидывании `undefined` компонент будет считаться `Uncontrolled`. > > Не используйте `undefined`, чтобы показать невыбранное состояние. Вместо этого используйте `null`. |

### CustomSelectOption

| Свойство | Тип | По умолчанию | Описание |
| --- | --- | --- | --- |
| `after` | `ReactNode` | `-` | Вставляет элемент в конец блока после основного контента. Например, можно передать компонент `Avatar`, `Icon<Name>` или другие изображения. |
| `before` | `ReactNode` | `-` | Вставляет элемент в начало блока перед основным контентом. Например, можно передать компонент `Avatar`, `Icon<Name>` или другие изображения. |
| `description` | `ReactNode` | `-` | Добавляет описание под основным блоком. |
| `disabled` | `boolean` | `-` | Блокирует весь блок.  > ⚠️  Важно: если CustomSelectOption используется внутри [Select](https://vkui.io/components/select), [CustomSelect](https://vkui.io/components/custom-select) или [ChipsSelect](https://vkui.io/components/chips-select), то свойство явно должно выставляться только через структуру `options`. > Запрещается выставлять `disabled` проп опциям в обход `options`, иначе [CustomSelect](https://vkui.io/components/custom-select) и [ChipsSelect](https://vkui.io/components/chips-select) не будут знать об актуальном состоянии опции. |
| `focused` | `boolean` | `-` | Включает состояние фокуса. |
| `getRootRef` | `Ref<HTMLDivElement>` | `-` |  |
| `hierarchy` | `number` | `0` | Позволяет создавать вложенность. |
| `hovered` | `boolean` | `-` | Включает состояние наведения. |
| `selected` | `boolean` | `-` | Включает состояние выбранного элемента списка. |
| `textNoWrap` | `boolean` | `-` | Предотвращает перенос текста внутри опции. |

