Radix UI построен вокруг идеи композиции и повторного использования интерфейсных примитивов. Библиотека не предоставляет готовые визуальные компоненты, а поставляет поведенческие блоки, которые можно многократно использовать в разных частях приложения.
Переиспользование достигается за счёт нескольких ключевых механизмов:
Такая архитектура позволяет строить сложные интерфейсы из небольших универсальных элементов.
Компоненты Radix представляют собой headless primitives — элементы без встроенного дизайна. Они содержат:
Пример структуры компонента:
import * as Dialog from "@radix-ui/react-dialog";
<Dialog.Root>
<Dialog.Trigger>Открыть</Dialog.Trigger>
<Dialog.Content>
<Dialog.Title>Заголовок</Dialog.Title>
</Dialog.Content>
</Dialog.Root>
Каждый элемент (Root, Trigger,
Content) — самостоятельный компонент,
который может быть переиспользован.
Radix активно использует паттерн compound components — набор взаимосвязанных компонентов, которые работают внутри общего контекста.
Типичная структура:
Component.Root
Component.Trigger
Component.Content
Component.Item
Component.Label
Например:
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
<DropdownMenu.Root>
<DropdownMenu.Trigger>Меню</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item>Профиль</DropdownMenu.Item>
<DropdownMenu.Item>Настройки</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
Каждый элемент можно оборачивать, стилизовать или расширять, сохраняя базовую логику.
На практике компоненты Radix редко используются напрямую. Чаще создаются обёртки, инкапсулирующие стили и поведение.
Пример создания кнопки для триггера меню:
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
export function MenuButton({ children }) {
return (
<DropdownMenu.Trigger className="menu-button">
{children}
</DropdownMenu.Trigger>
);
}
Теперь компонент можно использовать многократно:
<DropdownMenu.Root>
<MenuButton>Открыть меню</MenuButton>
<DropdownMenu.Content>
<DropdownMenu.Item>Профиль</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
asChildОдна из ключевых возможностей Radix — проп
asChild. Он позволяет заменить DOM-элемент
компонента Radix собственным компонентом.
asChildБез asChild компонент создаёт собственный
DOM-элемент:
<Dialog.Trigger>
<button>Открыть</button>
</Dialog.Trigger>
В DOM получается вложенная структура:
button
button
Это нарушает семантику HTML.
asChild<Dialog.Trigger asChild>
<button className="button">
Открыть
</button>
</Dialog.Trigger>
Теперь Radix передаёт поведение дочернему элементу, не создавая лишнего DOM.
Преимущества:
Компоненты интерфейса можно объединять с примитивами Radix.
function Button({ children, ...props }) {
return (
<button className="ui-button" {...props}>
{children}
</button>
);
}
Использование с Radix:
<Dialog.Trigger asChild>
<Button>Открыть диалог</Button>
</Dialog.Trigger>
Кнопка остаётся универсальным компонентом, а Radix добавляет поведение.
Radix позволяет создавать более высокоуровневые компоненты, скрывающие внутреннюю структуру.
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
export function Menu({ trigger, children }) {
return (
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
{trigger}
</DropdownMenu.Trigger>
<DropdownMenu.Content className="menu">
{children}
</DropdownMenu.Content>
</DropdownMenu.Root>
);
}
Использование:
<Menu trigger={<Button>Меню</Button>}>
<DropdownMenu.Item>Профиль</DropdownMenu.Item>
<DropdownMenu.Item>Выход</DropdownMenu.Item>
</Menu>
Такая абстракция уменьшает повторение кода.
Логику компонентов можно выносить в кастомные хуки, что упрощает переиспользование.
Пример:
function useMenuState() {
const [open, setOpen] = React.useState(false);
return {
open,
onOpenChange: setOpen
};
}
Использование:
function MenuComponent() {
const menu = useMenuState();
return (
<DropdownMenu.Root {...menu}>
...
</DropdownMenu.Root>
);
}
Хук можно применять в разных компонентах.
Radix-компоненты позволяют создавать повторяемые UI-шаблоны.
Например, элементы меню.
function MenuItem({ children, ...props }) {
return (
<DropdownMenu.Item
className="menu-item"
{...props}
>
{children}
</DropdownMenu.Item>
);
}
Использование:
<MenuItem>Редактировать</MenuItem>
<MenuItem>Удалить</MenuItem>
<MenuItem>Копировать</MenuItem>
Преимущества:
Radix поощряет композицию, при которой сложные компоненты собираются из простых.
Menu
├── MenuTrigger
├── MenuContent
│ ├── MenuItem
│ ├── MenuItem
│ └── MenuSeparator
Каждый элемент может быть:
Поскольку Radix не содержит стилей, внешний вид компонентов определяется системой стилей приложения.
Популярные подходы:
.menu-item {
padding: 8px 12px;
cursor: pointer;
}
.menu-item:hover {
background: #f3f3f3;
}
Это позволяет использовать один и тот же компонент во множестве интерфейсов.
Можно создавать layout-компоненты, содержащие Radix внутри.
Пример: ModalLayout.
function ModalLayout({ title, children }) {
return (
<Dialog.Content className="modal">
<Dialog.Title>{title}</Dialog.Title>
<div className="modal-body">
{children}
</div>
</Dialog.Content>
);
}
Использование:
<Dialog.Root>
<Dialog.Trigger asChild>
<Button>Открыть</Button>
</Dialog.Trigger>
<ModalLayout title="Настройки">
<SettingsForm />
</ModalLayout>
</Dialog.Root>
Radix позволяет создавать компоненты с слотами.
Пример:
function Card({ header, footer, children }) {
return (
<div className="card">
<div className="card-header">{header}</div>
<div className="card-body">
{children}
</div>
<div className="card-footer">
{footer}
</div>
</div>
);
}
Использование:
<Card
header="Заголовок"
footer={<Button>Сохранить</Button>}
>
Контент карточки
</Card>
Такая архитектура облегчает повторное использование.
Radix активно использует React Context для передачи состояния между частями компонента.
Это позволяет компонентам:
Например, DropdownMenu.Item автоматически получает
контекст меню.
Благодаря этому элементы можно свободно комбинировать.
При разработке системы UI на основе Radix часто применяется трёхуровневая архитектура.
Низкоуровневые компоненты:
Dialog
Popover
DropdownMenu
Tabs
Tooltip
Компоненты со стилями:
Modal
Menu
Tabs
Tooltip
Select
Компоненты приложения:
UserMenu
SettingsModal
ProductTabs
ProfileDropdown
Такой подход обеспечивает:
Типичная структура проекта:
components
ui
button
Button.jsx
dialog
Modal.jsx
menu
Menu.jsx
shared
MenuItem.jsx
FormField.jsx
Radix используется внутри ui-слоя, а бизнес-компоненты строятся поверх него.
Использование Radix UI для построения переиспользуемых компонентов даёт ряд преимуществ:
Предсказуемость интерфейсов
Все компоненты используют одинаковые примитивы.
Доступность
ARIA-поведение уже реализовано.
Минимизация дублирования
Повторное использование снижает объём кода.
Масштабируемость
Компоненты легко расширяются и комбинируются.
Гибкость
Отсутствие встроенных стилей позволяет адаптировать компоненты под любую дизайн-систему.
Слишком универсальные компоненты становятся сложными.
Компоненты не должны зависеть от конкретного контекста приложения.
Лучше комбинировать маленькие компоненты, чем создавать один большой.
Эффективное использование Radix UI строится на нескольких принципах:
Такая архитектура позволяет строить крупные интерфейсы из небольших, независимых и легко переиспользуемых компонентов.