Skip to main content
Week 02 • Track 07 • The Process

The Session
Musicians.

Don't build the drums. Play them. Stop writing raw CSS for buttons and inputs. Use a Component Library to keep your Front of House clean.

npm install
components/ui
import
Render

The Mental Model

You are the Conductor.

A junior dev tries to sculpt every button from clay (CSS). A senior dev picks up a Lego block that already works.

1

Headless UI handles accessibility, keyboard nav, and focus states.

2

Tailwind provides the styling layer on top.

3

You compose pre-built pieces into custom UIs.

Accessible
Composable
Standardized
Button
Email...
Card
components/ui/*

The Studio

Key Concept
shadcn/ui

It's not a library. It's code.

Unlike Bootstrap, you don't just link a file. You copy the code into your project. You own it. If you want the button to be blue, you change the code in your folder.

  • BenefitFull control. No fighting the library's opinions.
  • BenefitNo version lock-in. The code is yours forever.
  • TradeoffYou maintain the code. No auto-updates.
$ npx shadcn@latest add button
... installing components/ui/button.tsx

Front of House vs. Kitchen

Keep your "Atoms" (small parts) separate from your "Pages" (views).

📁 app // Routes (Pages)
📁 dashboard
📄 page.tsx
📁 settings
📄 page.tsx
📁 components // The Lego Bin
📁 ui // Dumb (Button, Input)
📄 button.tsx
📄 input.tsx
📁 dashboard // Smart (UserChart)
📁 lib // Utilities
📄 utils.ts
Clean Code
@

The Import Alias

Stop doing "dot dot slash" gymnastics. Use the @ symbol to reach the root of your project instantly.

import Button from "../../../components/ui/button"
import { Button } from "@/components/ui/button"
Architecture
🧠

Dumb vs. Smart Components

Separate presentation from logic for maximum reusability.

Dumb (UI) components/ui/

No state, no API calls. Just props in, HTML out. Button, Input, Card.

Smart (Feature) components/dashboard/

Has state, fetches data, handles logic. UserProfile, CheckoutForm.

The Components

shadcn/ui provides dozens of pre-built components. Here are the essential ones you'll use in almost every project.

Essential

Form Inputs

The building blocks of any interactive app.

Button
Input
Textarea
Select
Structure

Layout

Containers and structural components.

Card
Separator
Tabs
Accordion
Feedback

User Feedback

Tell users what's happening.

Toast
Alert
Badge
Progress
Overlay

Modals & Menus

Content that appears above the page.

Dialog
Sheet
Dropdown Menu
Popover

Quick Install

$ npx shadcn@latest add button input card dialog toast

# Installs 5 components at once

The Customization

Variants (The Right Way)

shadcn uses class-variance-authority (cva) to define variants. Add new styles without breaking existing ones.

// components/ui/button.tsx

const buttonVariants = cva(

"inline-flex items-center...",

{

variants: {

variant: {

default: "bg-primary...",

destructive: "bg-red-500...",

outline: "border...",

// Add your own!

brand: "bg-purple-600...",

},

},

}}

)

Global Theming

Change colors site-wide by editing CSS variables in globals.css. All components inherit automatically.

/* globals.css */

:root {

--primary: 222.2 47.4% 11.2%;

--primary-foreground: 210 40% 98%;

--radius: 0.5rem;

}

.dark {

--primary: 210 40% 98%;

--primary-foreground: 222.2 47.4% 11.2%;

}

Tip: shadcn uses HSL values without the hsl() wrapper for Tailwind compatibility.

Common Patterns

Anti-Patterns

Overriding with !important

If you need !important, you're fighting the library. Edit the source instead.

Wrapping in extra divs

Components already have proper styling. Use className prop, not wrapper divs.

Duplicating component code

Need a variant? Add it to the existing component, don't copy-paste.

Ignoring TypeScript props

Components have typed props. Use them for autocomplete and safety.

Best Practices

Use the className prop

<Button className="w-full"> extends styles cleanly.

Add variants for reuse

Need a "brand" button? Add a variant, don't inline styles.

Compose, don't modify

Build complex UIs by combining simple components.

Tell AI about your components

"Use Button from @/components/ui/button" in every prompt.

The Exercises

Assemble the stage.

01

The Hire

Install Components

  • npx shadcn@latest init
  • npx shadcn@latest add button card input
  • Check your components/ui folder
  • Read the button.tsx code
Goal: Understand you own the code
02

The Jam

Use with AI

  • Open Claude or ChatGPT
  • "Build a login form using Button, Input, Card from @/components/ui"
  • Paste the code into page.tsx
  • See how clean the file looks
Goal: AI + component library = clean code
03

The Remix

Customize

  • Open components/ui/button.tsx
  • Find the "variants" object
  • Add a new variant called "brand"
  • Use it: <Button variant="brand">
Goal: Learn to extend, not override

📋 Quick Reference Cheatsheet

Initialize

npx shadcn@latest init

Add Component

npx shadcn@latest add [name]

Import Pattern

import { X } from "@/components/ui/x"