Skip to content
Building UI Design Systems

Lesson 2 of 6 · 25 min

x
2/6

Lesson position in the course — not completion. Use Mark Complete to track finished lessons (saved in this browser).

Setting Up shadcn/ui & Tailwind CSS

Unlike traditional component libraries distributed as compiled npm packages (e.g., Material UI, Chakra), shadcn/ui provides un-styled, accessible component primitives that are copied directly into your source repository. You own the code, allowing complete customization of styles and DOM structure.

shadcn/ui combines Radix UI headless primitives (handling ARIA attributes and focus management) with Tailwind CSS for utility styling. Using the cn() helper utility (clsx + tailwind-merge), components gracefully merge default library styles with custom caller-provided className overrides.

Before
Class Collisions with Standard String Concatenation
1// ❌ Breaks when caller passes competing padding classes!2const className = "px-4 py-2 " + props.className;
After
Tailwind Merge Helper (`cn`)
1import { clsx, type ClassValue } from 'clsx';2import { twMerge } from 'tailwind-merge';3 4// ✅ Safely resolves utility class collisions5export function cn(...inputs: ClassValue[]) {6  return twMerge(clsx(inputs));7}8 9// Example: cn("px-4 py-2 bg-blue-500", "px-6") -> "py-2 bg-blue-500 px-6"

Exercise

Initialize a custom Button component using cn() that merges base styles with variant and size props.

Check your understanding

  • How does shadcn/ui differ from traditional UI component libraries?Show answer

    Answer

    Components are copied directly into your project source code rather than installed as an opaque npm dependency.
  • What role does tailwind-merge play in the cn() utility function?Show answer

    Answer

    It eliminates conflicting Tailwind utility classes passed in props (e.g., resolving px-4 and px-6 to px-6).
Previous

Progress is saved in this browser.

Next Lesson