Урок 16. Частые ошибки

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

⚡ Кратко

Ждать от реестра поведения библиотеки, бояться править скопированный код, обновлять компоненты вслепую и терять единообразие в команде.

Ошибка 1. Искать пакет shadcn/ui в зависимостях

Как проявляется: «а какую версию библиотеки мы используем?» — вопрос без ответа, потому что библиотеки нет.

Как правильно: версия у вас одна — та, что в момент копирования попала в файлы. Узнать различия с реестром можно так:

npx shadcn@latest add button --diff

Ошибка 2. Бояться менять скопированный код

Как проявляется: вместо правки компонента разработчик обвешивает его обёртками и переопределениями снаружи — и получает те же проблемы, что и с чужой библиотекой.

Как правильно: это ваш файл. Нужен новый вариант — добавьте в cva. Не нужен вариант — удалите. Именно ради этого исходник и копируется в проект.

Ошибка 3. Слепое повторное добавление

Как ломается: команда add --overwrite затирает ваши правки, и интерфейс возвращается к состоянию «из коробки».

npx shadcn@latest add button --diff        # сначала посмотреть отличия
npx shadcn@latest add button --dry-run     # затем — что именно изменится

Как правильно: компоненты живут в git. Перед обновлением — чистое рабочее дерево, после — обычный code review диффа.

Ошибка 4. Пять кнопок в одном проекте

Как проявляется: у каждого разработчика своя версия компонента, потому что «проще скопировать ещё раз».

Как правильно: договориться, что components/ui — общая зона, изменения в ней проходят ревью. Свобода правки не отменяет необходимости единого источника правды.

Ошибка 5. Игнорировать примитив

Как ломается: разработчик заменяет DialogPrimitive.Popup на обычный div, потому что «так проще», — и теряет фокус-ловушку, Esc и блокировку прокрутки.

Как правильно: менять оформление — да, менять слой поведения — только осознанно. Всё, что примитив делает незаметно, придётся реализовать самому.

Ошибка 6. Копировать код из статей под старую версию

Смешение API Radix и Base UI в одном проекте — источник странных ошибок: asChild не работает там, где ждут render, а data-[state=open] не срабатывает там, где примитив ставит data-open. Ориентируйтесь на исходник в своём проекте — он всегда актуальнее статьи.