Программное создание

Radix UI — это коллекция низкоуровневых компонентов для React, которые предоставляют полностью доступную и управляемую реализацию интерфейсных элементов. Ключевая цель библиотеки — обеспечить контроль над визуальным оформлением, оставляя при этом все доступные механизмы взаимодействия и а11y на уровне библиотеки.

Все компоненты построены на контролируемой модели состояния. Это означает, что можно управлять их поведением через пропсы или использовать внутреннее состояние компонента. Каждый компонент состоит из нескольких слоёв: Root, Trigger, Content, иногда дополнительные вспомогательные элементы (Portal, Arrow, Item и др.).

Пример базовой структуры Radix UI для диалогового окна (Dialog):

import * as Dialog from '@radix-ui/react-dialog';

<Dialog.Root>
  <Dialog.Trigger>Открыть диалог</Dialog.Trigger>
  <Dialog.Portal>
    <Dialog.Overlay className="overlay" />
    <Dialog.Content className="content">
      <Dialog.Title>Заголовок</Dialog.Title>
      <Dialog.Description>Описание диалога</Dialog.Description>
      <Dialog.Close>Закрыть</Dialog.Close>
    </Dialog.Content>
  </Dialog.Portal>
</Dialog.Root>

Здесь Dialog.Root управляет состоянием открыто/закрыто, Dialog.Trigger инициирует открытие, а Dialog.Content содержит видимую часть. Dialog.Portal позволяет выводить компонент в отдельный слой DOM, что облегчает работу с модальными окнами и слоями поверх контента.


Контролируемое и неконтролируемое состояние

Radix UI предоставляет два способа управления компонентами:

  1. Контролируемое состояние — компонент полностью управляется внешними пропсами. Пример для селекта:
import * as Sel ect from '@radix-ui/react-select';
import { useState } fr om 'react';

const ControlledSelect = () => {
  const [value, setValue] = useState('option1');
  
  return (
    <Sel ect.Root value={value} onValueCha nge={setValue}>
      <Sel ect.Trigger>{value}</Sel ect.Trigger>
      <Select.Content>
        <Select.Item value="option1">Опция 1</Select.Item>
        <Select.Item value="option2">Опция 2</Select.Item>
      </Select.Content>
    </Select.Root>
  );
};
  1. Неконтролируемое состояние — библиотека сама управляет открытием/закрытием или выбранными значениями, но предоставляет события для реагирования на изменения.

Использование контролируемого состояния важно для сложных интерфейсов с синхронизацией состояния между компонентами.


Управление порталами и слоями

Radix UI активно использует порталы, чтобы выводить элементы поверх других слоёв DOM. Это особенно актуально для Popover, Dialog и Tooltip.

Ключевые моменты:

  • Dialog.Portal и Popover.Portal создают отдельный слой, позволяющий обрабатывать z-index и анимации без вмешательства в основной DOM.
  • Overlay используется для затемнения фона и перехвата кликов, предотвращая взаимодействие с контентом под модальным окном.
  • Все элементы внутри портала сохраняют доступность для экранных читалок.

Анимации и стилизация

Radix UI не навязывает визуальные стили, поэтому компоненты можно полностью кастомизировать через CSS, Tailwind или CSS-in-JS. Для анимаций удобно использовать Radix Primitives + Framer Motion:

import { motion } fr om 'framer-motion';
import * as Dialog fr om '@radix-ui/react-dialog';

<Dialog.Content asChild>
  <motion.div
    initial={{ opacity: 0, y: -10 }}
    animate={{ opacity: 1, y: 0 }}
    exit={{ opacity: 0, y: -10 }}
  >
    Контент диалога с анимацией
  </motion.div>
</Dialog.Content>

Использование asChild позволяет передать любой компонент для рендеринга, сохраняя управление состоянием и а11y.


Интеграция с формами

Компоненты Radix UI удобно использовать вместе с формами и библиотеками для управления формами, такими как React Hook Form или Formik. Селект, радио и чекбоксы интегрируются через onValueChange и value. Пример с React Hook Form:

import { useForm, Controller } fr om 'react-hook-form';
import * as Sel ect fr om '@radix-ui/react-select';

const FormWithSelect = () => {
  const { control, handleSubmit } = useForm({ defaultValues: { choice: '' } });

  return (
    <form onSub mit={handleSubmit(console.log)}>
      <Controller
        control={control}
        name="choice"
        render={({ field }) => (
          <Select.Root {...field}>
            <Select.Trigger>{field.value || 'Выберите'}</Select.Trigger>
            <Select.Content>
              <Select.Item value="one">Один</Select.Item>
              <Select.Item value="two">Два</Select.Item>
            </Select.Content>
          </Select.Root>
        )}
      />
      <button type="submit">Отправить</button>
    </form>
  );
};

Обработка событий и расширение функциональности

Radix UI предоставляет набор событий для каждого компонента: onOpenChange, onValueChange, onFocus, onBlur. Эти события можно использовать для:

  • Синхронизации состояния между компонентами
  • Валидации пользовательского ввода
  • Запуска кастомной логики при открытии/закрытии модальных окон

Для расширения функциональности применяются композиционные паттерны: можно оборачивать Radix-компоненты в собственные контроллеры или провайдеры контекста, добавляя бизнес-логику, не нарушая базовую а11y и поведение.


Примеры продвинутого использования

  1. Комбинированные селекты с фильтрацией и кастомной рендеринговой логикой
  2. Многоуровневые меню (Dropdown Menu) с управлением фокусом и keyboard navigation
  3. Модальные окна с асинхронной подгрузкой контента и анимацией появления/исчезания

В каждом случае ключевой принцип — Radix обеспечивает логику и доступность, а разработчик управляет визуальной частью и бизнес-логикой, создавая гибкие и масштабируемые интерфейсы.


Radix UI в JavaScript обеспечивает мощный набор низкоуровневых компонентов, позволяя строить сложные интерактивные интерфейсы с контролем над состоянием, анимациями и доступностью, сохраняя полную свободу стилизации и интеграции с экосистемой React.