Урок 16. Старый vs Новый: библиотека и реестр, Radix и Base UI

📁 Раздел: shadcn/ui ⏱️ Время изучения: ~40 мин 🎯 Сложность: Средняя

⚡ Кратко

Классическая библиотека даёт компонент как чёрный ящик, shadcn/ui — как исходник. Кроме того, сам shadcn/ui изменился: современные версии строятся на Base UI, а не на Radix, и используют render вместо asChild.

Кастомизация кнопки

❌ Библиотека: переопределяем чужие стили

// каждая библиотека предлагает свой способ, и все они разные
<Button sx={{ borderRadius: 12, textTransform: "none" }}>Кнопка</Button>

// либо тема на весь проект
createTheme({ components: { MuiButton: { styleOverrides: { root: { … } } } } })

Работает, пока задача укладывается в предусмотренный API. Дальше начинается борьба со специфичностью и «!important».

✅ Реестр: правим свой файл

// src/components/ui/button.tsx
const buttonVariants = cva("inline-flex items-center rounded-lg …", {
  variants: { variant: { default: "bg-primary …" } },
})

Нет «API кастомизации» — есть код. Это одновременно самая сильная и самая ответственная сторона подхода.

Внутри самого shadcn/ui: Radix → Base UI

ЧтоСтарые версии (Radix)Актуальные (Base UI)
Пакет примитивов@radix-ui/react-*@base-ui/react
ПолиморфизмasChild + Slotпроп render
Стиль по умолчаниюdefault / new-yorkbase-nova
Части оверлеяOverlay, ContentBackdrop, Popup
Состояния в атрибутахdata-[state=open]data-open, data-closed
// старый способ подменить тег кнопки-триггера
<DialogTrigger asChild>
  <Button>Открыть</Button>
</DialogTrigger>

// современный
<DialogTrigger render={<Button>Открыть</Button>} />
Это главная причина расхождений между курсом и статьями в интернете. Увидели asChild и @radix-ui/* — перед вами материал под старую версию. Он не «неправильный», но API другое; при желании ту же базу можно поставить флагом --base radix.

Что не изменилось

  • Идея реестра: код копируется в проект.
  • Связка cva + cn() для вариантов.
  • Семантические токены темы и тёмный режим по классу.
  • Композиция из мелких подкомпонентов вместо пропсов-флагов.