Урок 17. Теория: init, алиасы, структура проекта

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

⚡ Кратко

init не «устанавливает библиотеку», а настраивает проект: записывает components.json, дописывает токены темы в ваш CSS, создаёт lib/utils.ts с функцией cn() и ставит необходимые пакеты. Дальше каждая команда add просто кладёт файлы компонентов по путям из конфигурации.

Порядок установки

  1. проект (Vite, Next.js, Astro, React Router — любой из поддерживаемых);
  2. Tailwind CSS и его плагин сборки;
  3. алиас @/ в конфигурации сборщика и в TypeScript;
  4. npx shadcn@latest init;
  5. npx shadcn@latest add … — по мере надобности.
Пункт 3 нельзя пропустить: CLI проверяет алиас и откажется работать без него. Причина простая — компоненты импортируют cn() как @/lib/utils, и этот путь должен разрешаться и сборщиком, и редактором.

Шаг за шагом на Vite

npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
npm install tailwindcss @tailwindcss/vite
npm install -D @types/node
📄 src/index.css
@import "tailwindcss";
📄 vite.config.ts
// vite.config.ts
import path from "path"
import tailwindcss from "@tailwindcss/vite"
import react from "@vitejs/plugin-react"
import { defineConfig } from "vite"

export default defineConfig({
  plugins: [react(), tailwindcss()],
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
})
📄 tsconfig.json
// tsconfig.json — и то же самое в tsconfig.app.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

Пакет @types/node нужен только затем, чтобы TypeScript понимал path.resolve и __dirname в конфигурации Vite.

Что делает init

npx shadcn@latest init

✔ Preflight checks.
✔ Verifying framework. Found Vite.
✔ Validating Tailwind CSS. Found v4.
✔ Validating import alias.
✔ Writing components.json.
✔ Checking registry.
✔ Installing dependencies.
✔ Created 2 files:
  - src/components/ui/button.tsx
  - src/lib/utils.ts
✔ Updating src/index.css

Project initialization completed.
You may now add components.

Разберём вывод по строчкам:

  • Verifying framework — CLI определяет, на чём проект;
  • Validating Tailwind CSS — находит версию (v4) и главный CSS-файл;
  • Validating import alias — проверяет тот самый алиас @/;
  • Writing components.json — сохраняет ответы на будущее;
  • Installing dependencies — ставит примитивы, cva, clsx, tailwind-merge, иконки;
  • Created 2 files — базовый компонент и cn();
  • Updating index.css — дописывает токены темы и @theme inline.

init — как обустройство мастерской перед работой: он не делает мебель, а расставляет верстак, свет и инструменты и записывает, где что лежит. Каждая последующая команда add — это уже изготовление конкретной детали по чертежу из реестра.

Что появилось в CSS

Главный файл стилей после init содержит четыре блока:

📄 src/index.css
@import "tailwindcss";
@import "tw-animate-css";          /* анимации оверлеев */
@import "shadcn/tailwind.css";     /* базовые стили реестра */

@custom-variant dark (&:is(.dark *));

@theme inline {
  --color-background: var(--background);
  --color-foreground: var(--foreground);
  /* … и так для всех ролей */
  --radius-lg: var(--radius);
}

:root  { --background: oklch(1 0 0); --foreground: oklch(0.145 0 0); /* … */ }
.dark  { --background: oklch(0.145 0 0); --foreground: oklch(0.985 0 0); /* … */ }

@layer base {
  * { @apply border-border outline-ring/50; }
  body { @apply bg-background text-foreground; }
}

Узнаёте? Это ровно тот приём с семантическими токенами, который мы разбирали в уроке 13 — только набор ролей готовый и проверенный.

Алиасы: почему @/ и что настроить

Алиас должен быть прописан дважды, и это разные вещи:

ГдеЗачемЧто будет без этого
vite.config.tsсборщик разрешает путь при сборкеошибка сборки: модуль не найден
tsconfig.jsonTypeScript и редактор понимают путькрасные подчёркивания и отсутствие автодополнения
В шаблоне Vite настройки разнесены по tsconfig.json и tsconfig.app.json. Пропишите baseUrl и paths в обоих — иначе редактор будет подсвечивать импорты как ошибочные, хотя сборка пройдёт.

Добавление компонентов

npx shadcn@latest add button card input label
npx shadcn@latest add dialog dropdown-menu tabs table

CLI ставит недостающие зависимости и создаёт файлы. Если компонент зависит от другого (например, Dialog использует Button), он добавится автоматически.

src/components/ui/
├── button.tsx
├── card.tsx
├── dialog.tsx
├── dropdown-menu.tsx
├── input.tsx
├── label.tsx
├── table.tsx
└── tabs.tsx

Полезные режимы add

КомандаКогда пригодится
add button --dry-runпосмотреть, что произойдёт, ничего не меняя
add button --diffсравнить свой файл с версией реестра
add button --overwriteперезаписать (осторожно: сотрёт правки)
add --allпоставить весь набор — удобно для изучения
view buttonпрочитать исходник, не добавляя в проект

Проверка результата

📄 src/App.tsx
import { Button } from "@/components/ui/button"

export default function App() {
  return (
    <main className="flex min-h-svh items-center justify-center">
      <Button>Click me</Button>
    </main>
  )
}
<div class="flex min-h-24 items-center justify-center rounded-lg bg-background">
  <button class="inline-flex h-8 items-center justify-center rounded-lg bg-primary px-2.5 text-sm font-medium text-primary-foreground transition-all hover:bg-primary/80">
    Click me
  </button>
</div>

Если кнопка выглядит оформленной — значит, тема подключилась и алиасы работают. Если это обычная системная кнопка — CSS с токенами не импортирован в приложение.