Radix UI — это набор низкоуровневых UI-примитивов для React, ориентированных на создание доступных и полностью контролируемых интерфейсов. Архитектура приложения, использующего Radix UI, строится вокруг композиции компонентов, разделения ответственности и строгого контроля состояния.
Основная идея заключается в том, что библиотека предоставляет поведенческие и доступные primitives, а визуальное оформление и структура интерфейса полностью остаются в зоне ответственности приложения.
Ключевые архитектурные принципы:
1. Композиция вместо монолитных компонентов
Radix UI разбивает сложные интерфейсные элементы на набор независимых частей.
Пример структуры компонента Dialog:
Dialog.Root
Dialog.Trigger
Dialog.Portal
Dialog.Overlay
Dialog.Content
Dialog.Title
Dialog.Description
Dialog.Close
Каждый элемент отвечает только за одну функцию:
Такая декомпозиция позволяет строить сложные интерфейсы, не нарушая архитектурную чистоту приложения.
При разработке приложений с Radix UI чаще всего применяется многоуровневая структура интерфейса.
Типичная архитектура включает следующие уровни:
Application Layer
↓
Feature Components
↓
Design System
↓
Radix Primitives
↓
DOM
Верхний уровень приложения. Содержит:
Пример:
app/
├─ pages/
│ ├─ dashboard
│ └─ settings
├─ providers
└─ layout
На этом уровне Radix UI напрямую почти не используется.
Компоненты бизнес-логики, объединяющие несколько элементов интерфейса.
Примеры:
Такие компоненты используют готовые элементы дизайн-системы, которые в свою очередь основаны на Radix UI.
features/
├─ user-menu
├─ settings-dialog
└─ notifications
Центральный уровень архитектуры. Здесь создаются обертки над Radix primitives, формирующие единый стиль интерфейса.
Например:
ui/
├─ button
├─ dialog
├─ dropdown-menu
├─ sel ect
└─ tooltip
Каждый компонент инкапсулирует:
Пример обертки Dialog.
import * as Dialog fr om "@radix-ui/react-dialog"
export function Modal({ children, trigger }) {
return (
<Dialog.Root>
<Dialog.Trigger asChild>
{trigger}
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="overlay" />
<Dialog.Content className="modal">
{children}
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
)
}
В результате приложение использует Modal, а не напрямую Radix.
Нижний уровень — собственно библиотека Radix UI.
Этот слой предоставляет:
Благодаря этому дизайн-система может сосредоточиться только на логике и визуальном стиле.
Radix UI придерживается controlled/uncontrolled модели состояния.
Компоненты могут работать:
1. Неконтролируемо
<Dialog.Root>
Состояние открытия управляется внутри Radix.
2. Контролируемо
const [open, setOpen] = useState(false)
<Dialog.Root open={open} onOpenCha nge={setOpen}>
Состояние контролируется приложением.
Такая архитектура позволяет:
Radix UI активно использует React Context для передачи состояния между примитивами.
Пример:
Dialog.Root
├─ Dialog.Trigger
├─ Dialog.Content
└─ Dialog.Close
Внутри:
Это позволяет:
Многие компоненты Radix используют React Portal.
Особенно:
Пример:
<Dialog.Portal>
<Dialog.Content />
</Dialog.Portal>
Portal перемещает DOM-узел в конец документа:
<body>
<div id="root"></div>
<div data-radix-portal>
dialog content
</div>
</body>
Это решает несколько архитектурных проблем:
1. Z-index конфликты
Модальные окна всегда поверх интерфейса.
2. overflow hidden
Выпадающие элементы не обрезаются контейнерами.
3. изоляция слоев
Компоненты рендерятся независимо от структуры DOM.
Radix UI изначально проектировался как доступная библиотека интерфейсов.
Архитектура компонентов включает:
Пример для Dialog:
role="dialog"
aria-labelledby
aria-describedby
При открытии:
Все эти механизмы встроены в архитектуру Radix.
Компоненты вроде:
используют библиотеку Floating UI для позиционирования.
Архитектура выглядит следующим образом:
Radix Component
↓
Floating UI
↓
DOM measurements
↓
position calculation
Floating UI вычисляет:
Например:
<Tooltip.Content side="top" align="center">
Radix автоматически:
Одной из ключевых архитектурных возможностей Radix является asChild.
Он позволяет заменить DOM-элемент компонента.
Пример:
<Dialog.Trigger asChild>
<button className="custom-button">
Open
</button>
</Dialog.Trigger>
Без asChild:
<button>
<button>Open</button>
</button>
С asChild:
<button class="custom-button">
Open
</button>
Это критически важно для архитектуры дизайн-системы, потому что:
Radix UI проектировался вокруг принципа headless components.
Это означает, что:
Пример Dropdown Menu:
<DropdownMenu.Root>
<DropdownMenu.Trigger>Menu</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item>Edit</DropdownMenu.Item>
<DropdownMenu.Item>Delete</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
Radix обеспечивает:
Но не содержит ни одного CSS-правила.
При росте проекта архитектура на базе Radix обычно организуется следующим образом.
src
├─ app
├─ pages
├─ features
├─ widgets
├─ shared
│ └─ ui
│ ├─ button
│ ├─ dialog
│ ├─ select
│ ├─ tooltip
│ └─ dropdown-menu
└─ lib
Такой подход обеспечивает:
Radix UI не диктует систему стилизации.
На практике используются:
Часто применяется архитектура:
component.tsx
styles.css
index.ts
Пример:
dialog/
├─ dialog.tsx
├─ dialog.css
└─ index.ts
CSS:
.dialogOverlay {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.5);
}
.dialogContent {
background: white;
border-radius: 8px;
}
Radix UI предоставляет расширенные обработчики событий:
onOpenChange
onValueChange
onCheckedChange
onHighlightChange
Пример:
<DropdownMenu.Root
onOpenCha nge={(open) => {
console.log(open)
}}
>
Такая архитектура обеспечивает:
Radix содержит отдельные механизмы управления фокусом.
Они решают проблемы:
Пример:
<Dialog.Content
onOpenAutoFo cus={(event) => {
event.preventDefault()
}}
>
Это позволяет полностью контролировать фокус внутри компонентов.
Компоненты Radix легко расширяются благодаря композиции.
Пример расширения Dropdown Menu:
function UserMenu() {
return (
<DropdownMenu.Root>
<DropdownMenu.Trigger>
Avatar
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item>
Profile
</DropdownMenu.Item>
<DropdownMenu.Item>
Settings
</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Item>
Logout
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
)
}
Такая архитектура позволяет:
Radix UI распространяется как набор отдельных npm-пакетов.
Например:
@radix-ui/react-dialog
@radix-ui/react-dropdown-menu
@radix-ui/react-tooltip
@radix-ui/react-select
Это дает преимущества:
При правильной организации архитектуры Radix UI становится фундаментом дизайн-системы.
Принцип выглядит следующим образом:
Radix primitives
↓
UI components
↓
Feature components
↓
Application
Каждый уровень:
Такой подход обеспечивает создание масштабируемых интерфейсов, в которых логика взаимодействия, доступность и управление состоянием централизованы на уровне библиотеки, а внешний вид и композиция интерфейса полностью контролируются архитектурой приложения.