Radix UI — это набор высококачественных, низкоуровневых компонентов для React, ориентированных на доступность и гибкость. Основная философия библиотеки заключается в том, чтобы предоставить базовые строительные блоки, которые можно расширять и кастомизировать под конкретные задачи. Компоненты Radix UI не навязывают визуальный стиль, что позволяет интегрировать их в любую систему дизайна.
Ключевой момент расширяемости — это composition over
inheritance. В Radix UI компоненты строятся через композицию:
основная логика находится в корневом компоненте, а визуальная часть и
дополнительные эффекты подключаются через дочерние элементы и
props. Такой подход позволяет добавлять новые функции без
изменения исходного кода библиотеки.
asChild для кастомных элементовПрактически каждый компонент Radix UI поддерживает проп
asChild. Этот проп позволяет заменять стандартный
DOM-элемент на любой другой, сохраняя при этом всю внутреннюю логику
компонента:
import * as Dialog from '@radix-ui/react-dialog';
import { Button } from './ui/Button';
<Dialog.Root>
<Dialog.Trigger asChild>
<Button>Открыть диалог</Button>
</Dialog.Trigger>
<Dialog.Content>
<Dialog.Title>Заголовок</Dialog.Title>
<Dialog.Description>Описание диалога</Dialog.Description>
</Dialog.Content>
</Dialog.Root>
Здесь Dialog.Trigger использует кастомную кнопку,
сохраняя доступность и управление состоянием диалога. Такой подход
позволяет интегрировать библиотеку в любую визуальную систему и
расширять её функциональность за счёт кастомных компонентов.
Radix UI активно использует React Context для передачи состояния между компонентами. Это открывает возможности для расширения поведения:
Пример расширения состояния Accordion:
import * as Accordion from '@radix-ui/react-accordion';
import { createContext, useContext } from 'react';
const AccordionAnalyticsContext = createContext(null);
function AccordionWithAnalytics({ children }) {
const logOpen = (id) => console.log(`Открыт аккордеон: ${id}`);
return (
<AccordionAnalyticsContext.Provider value={{ logOpen }}>
<Accordion.Root type="single" collapsible>
{children}
</Accordion.Root>
</AccordionAnalyticsContext.Provider>
);
}
function AccordionItemExtended({ id, children }) {
const { logOpen } = useContext(AccordionAnalyticsContext);
return (
<Accordion.Item value={id} onCl ick={() => logOpen(id)}>
{children}
</Accordion.Item>
);
}
Такое расширение позволяет добавлять новую функциональность, не нарушая базового поведения Radix UI.
Radix UI предоставляет непривязанные к стилю
компоненты, что позволяет интегрировать их с любыми CSS или
JS-анимациями. Часто используются библиотеки вроде
framer-motion для создания плавных переходов. Пример с
анимацией открытия Dialog:
import { motion } from 'framer-motion';
import * as Dialog from '@radix-ui/react-dialog';
<Dialog.Root>
<Dialog.Trigger>Открыть</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay asChild>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="overlay"
/>
</Dialog.Overlay>
<Dialog.Content asChild>
<motion.div
initial={{ y: -50, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: -50, opacity: 0 }}
className="content"
>
Контент диалога
</motion.div>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
В этом примере Radix UI управляет доступностью и состоянием диалога,
а framer-motion добавляет визуальные эффекты, что повышает
интерактивность интерфейса.
Для сложных сценариев можно обернуть компоненты Radix UI в собственные хуки, которые инкапсулируют дополнительное поведение:
import { useState } from 'react';
import * as Tabs from '@radix-ui/react-tabs';
function useEnhancedTabs(initialValue) {
const [value, setValue] = useState(initialValue);
const selectTab = (tabId) => {
console.log(`Выбран таб: ${tabId}`);
setValue(tabId);
};
return { value, selectTab, Tabs };
}
// Использование:
const { value, selectTab, Tabs: EnhancedTabs } = useEnhancedTabs('tab1');
<EnhancedTabs.Root value={value} onValueCha nge={selectTab}>
<EnhancedTabs.List>
<EnhancedTabs.Trigger value="tab1">Таб 1</EnhancedTabs.Trigger>
<EnhancedTabs.Trigger value="tab2">Таб 2</EnhancedTabs.Trigger>
</EnhancedTabs.List>
<EnhancedTabs.Content value="tab1">Контент 1</EnhancedTabs.Content>
<EnhancedTabs.Content value="tab2">Контент 2</EnhancedTabs.Content>
</EnhancedTabs.Root>
Такой подход позволяет добавлять аналитические события, сторонние эффекты и сложное управление состоянием без изменения исходного API Radix UI.
Radix UI не накладывает стили и полностью совместим с системами темизации, включая CSS Variables и CSS-in-JS. Для расширения функциональности темизации рекомендуется:
stitches или
tailwindcss для динамического изменения стилей.Пример применения темы через stitches:
import { styled } from '@stitches/react';
import * as Switch from '@radix-ui/react-switch';
const ThemedSwitch = styled(Switch.Root, {
backgroundColor: '$gray100',
'&[data-state="checked"]': { backgroundColor: '$primary' },
});
<ThemedSwitch>
<Switch.Thumb />
</ThemedSwitch>
Это позволяет поддерживать единый дизайн на уровне всей системы компонентов, сохраняя доступность и функциональность Radix UI.
asChild для полной замены
DOM-элементов.Эти техники обеспечивают гибкость и масштабируемость интерфейсов на базе Radix UI, позволяя строить сложные приложения, сохраняя при этом доступность, контроль состояния и повторное использование компонентов.