As your Tailwind projects grow, organizing components and utilities becomes crucial. While Tailwind encourages utility-first development, you'll need patterns for building reusable components, extracting common patterns, and maintaining clean, readable code. In this lesson, you'll learn component patterns, class organization strategies, and when to extract utilities into component classes.
Utility-First Philosophy
Understanding when to use utilities vs. component classes:
The Utility-First Approach
- Default to utilities: Start with utility classes in your markup
- Extract sparingly: Only create component classes when patterns repeat
- Keep flexibility: Utilities allow easy customization
- Avoid premature abstraction: Don't extract until you see duplication
When to Use Utilities
<!-- Good: One-off custom layout -->
<div class="flex items-center justify-between p-6 bg-white rounded-lg shadow">
<h2 class="text-xl font-bold text-gray-900">Title</h2>
<button class="text-blue-600 hover:text-blue-700">Action</button>
</div>
<!-- Good: Unique hero section -->
<section class="min-h-screen flex items-center justify-center bg-gradient-to-br from-purple-500 to-pink-500 px-4">
<div class="max-w-4xl mx-auto text-center">
<h1 class="text-6xl font-bold text-white mb-6">Welcome</h1>
<p class="text-xl text-white/90 mb-8">Get started today</p>
</div>
</section>
<!-- Good: Simple variations -->
<button class="px-6 py-3 bg-blue-500 hover:bg-blue-600 text-white rounded-lg">
Primary
</button>
<button class="px-6 py-3 bg-gray-200 hover:bg-gray-300 text-gray-900 rounded-lg">
Secondary
</button>When to Extract Components
@import "tailwindcss";
/* Extract when pattern repeats across project */
@layer components {
/* Button used in 10+ places */
.btn {
@apply px-6 py-3 rounded-lg font-semibold transition-all;
}
.btn-primary {
@apply btn bg-blue-600 hover:bg-blue-700 text-white shadow hover:shadow-lg;
}
/* Card pattern used everywhere */
.card {
@apply bg-white dark:bg-gray-800 rounded-lg shadow-lg overflow-hidden;
}
/* Badge used 20+ times */
.badge {
@apply inline-flex items-center px-3 py-1 rounded-full text-sm font-semibold;
}
.badge-success {
@apply badge bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400;
}
}Organizing Utility Classes
Structure long class lists for better readability:
Recommended Class Order
<!-- Category-based organization -->
<div class="
/* Layout */
flex items-center justify-between
relative z-10
/* Spacing */
p-6 gap-4
/* Sizing */
w-full max-w-4xl
/* Typography */
text-base font-semibold
/* Colors */
bg-white text-gray-900
/* Borders */
border border-gray-200 rounded-lg
/* Effects */
shadow-lg
/* Transitions */
transition-all duration-300
/* Interactions */
hover:shadow-xl hover:scale-105
/* Responsive */
md:p-8 lg:max-w-6xl
/* Dark mode */
dark:bg-gray-800 dark:text-white dark:border-gray-700
">
Content
</div>Multi-line Formatting
<!-- Format long class lists vertically -->
<button
class="
flex items-center gap-2
px-6 py-3
bg-blue-600 hover:bg-blue-700
text-white font-semibold
rounded-lg shadow
transition-all duration-200
hover:shadow-lg hover:-translate-y-0.5
active:translate-y-0
focus:outline-none focus:ring-4 focus:ring-blue-300
disabled:opacity-50 disabled:cursor-not-allowed
dark:bg-blue-500 dark:hover:bg-blue-600
"
>
<svg class="w-5 h-5">...</svg>
<span>Click Me</span>
</button>Using Template Strings (React/JSX)
// Organize with template literals
function Card({ variant = 'default' }) {
const baseClasses = "rounded-lg shadow-lg overflow-hidden transition-all";
const variants = {
default: "bg-white dark:bg-gray-800",
primary: "bg-blue-50 dark:bg-blue-900/20 border-2 border-blue-500",
success: "bg-green-50 dark:bg-green-900/20 border-2 border-green-500",
};
return (
<div className={`${baseClasses} ${variants[variant]}`}>
{/* Card content */}
</div>
);
}
// Or use clsx/classnames library
import clsx from 'clsx';
function Button({ variant, size, disabled }) {
return (
<button
className={clsx(
// Base styles
'inline-flex items-center gap-2 font-semibold rounded-lg transition-all',
// Size variants
size === 'sm' && 'px-4 py-2 text-sm',
size === 'md' && 'px-6 py-3 text-base',
size === 'lg' && 'px-8 py-4 text-lg',
// Color variants
variant === 'primary' && 'bg-blue-600 hover:bg-blue-700 text-white',
variant === 'secondary' && 'bg-gray-200 hover:bg-gray-300 text-gray-900',
// States
disabled && 'opacity-50 cursor-not-allowed',
)}
>
Button
</button>
);
}Button Component Patterns
Create a comprehensive button system:
Button Base & Variants
@import "tailwindcss";
@layer components {
/* Base button */
.btn {
@apply inline-flex items-center justify-center gap-2;
@apply px-6 py-3 font-semibold rounded-lg;
@apply transition-all duration-200;
@apply focus:outline-none focus:ring-4 focus:ring-offset-2;
@apply disabled:opacity-50 disabled:cursor-not-allowed;
}
/* Solid variants */
.btn-primary {
@apply btn bg-blue-600 hover:bg-blue-700 active:bg-blue-800;
@apply text-white shadow hover:shadow-lg;
@apply focus:ring-blue-300;
}
.btn-secondary {
@apply btn bg-gray-600 hover:bg-gray-700 active:bg-gray-800;
@apply text-white shadow hover:shadow-lg;
@apply focus:ring-gray-300;
}
.btn-success {
@apply btn bg-green-600 hover:bg-green-700 active:bg-green-800;
@apply text-white shadow hover:shadow-lg;
@apply focus:ring-green-300;
}
.btn-danger {
@apply btn bg-red-600 hover:bg-red-700 active:bg-red-800;
@apply text-white shadow hover:shadow-lg;
@apply focus:ring-red-300;
}
/* Outline variants */
.btn-outline {
@apply btn border-2 bg-transparent;
@apply hover:bg-gray-50 dark:hover:bg-gray-800;
}
.btn-outline-primary {
@apply btn-outline border-blue-600 text-blue-600;
@apply hover:bg-blue-50 dark:hover:bg-blue-900/20;
@apply focus:ring-blue-300;
}
/* Ghost variants */
.btn-ghost {
@apply btn bg-transparent shadow-none;
@apply hover:bg-gray-100 dark:hover:bg-gray-800;
}
/* Sizes */
.btn-sm {
@apply px-4 py-2 text-sm;
}
.btn-lg {
@apply px-8 py-4 text-lg;
}
.btn-xl {
@apply px-10 py-5 text-xl;
}
/* Icon button */
.btn-icon {
@apply btn p-3;
}
/* Block button */
.btn-block {
@apply btn w-full;
}
}<!-- Using button components -->
<button class="btn-primary">Primary Button</button>
<button class="btn-secondary">Secondary Button</button>
<button class="btn-success">Success Button</button>
<button class="btn-danger">Danger Button</button>
<!-- Outline variants -->
<button class="btn-outline-primary">Outline Primary</button>
<!-- Ghost variant -->
<button class="btn-ghost">Ghost Button</button>
<!-- Sizes -->
<button class="btn-primary btn-sm">Small</button>
<button class="btn-primary">Default</button>
<button class="btn-primary btn-lg">Large</button>
<button class="btn-primary btn-xl">Extra Large</button>
<!-- Icon button -->
<button class="btn-primary btn-icon">
<svg class="w-5 h-5">...</svg>
</button>
<!-- Block button -->
<button class="btn-primary btn-block">Full Width</button>
<!-- Disabled state -->
<button class="btn-primary" disabled>Disabled</button>
<!-- With icon -->
<button class="btn-primary">
<svg class="w-5 h-5">...</svg>
<span>With Icon</span>
</button>Card Component Patterns
Build flexible card components:
@import "tailwindcss";
@layer components {
/* Card base */
.card {
@apply bg-white dark:bg-gray-800 rounded-lg shadow-lg overflow-hidden;
@apply transition-all duration-300;
}
.card-hover {
@apply card hover:shadow-xl hover:-translate-y-1;
}
/* Card sections */
.card-header {
@apply px-6 py-4 border-b border-gray-200 dark:border-gray-700;
}
.card-body {
@apply p-6;
}
.card-footer {
@apply px-6 py-4 border-t border-gray-200 dark:border-gray-700;
@apply bg-gray-50 dark:bg-gray-900;
}
/* Card variants */
.card-bordered {
@apply card border-2 border-gray-200 dark:border-gray-700;
}
.card-elevated {
@apply card shadow-2xl;
}
/* Compact card */
.card-compact .card-header {
@apply px-4 py-3;
}
.card-compact .card-body {
@apply p-4;
}
.card-compact .card-footer {
@apply px-4 py-3;
}
}<!-- Basic card -->
<div class="card">
<div class="card-header">
<h3 class="text-xl font-bold text-gray-900 dark:text-white">Card Title</h3>
</div>
<div class="card-body">
<p class="text-gray-600 dark:text-gray-300">Card content goes here...</p>
</div>
<div class="card-footer">
<button class="btn-primary">Action</button>
</div>
</div>
<!-- Hoverable card -->
<div class="card-hover cursor-pointer">
<div class="card-body">
<h3 class="text-xl font-bold mb-2">Interactive Card</h3>
<p class="text-gray-600">Lifts on hover</p>
</div>
</div>
<!-- Card with image -->
<div class="card">
<img src="image.jpg" alt="Card" class="w-full h-48 object-cover">
<div class="card-body">
<h3 class="text-xl font-bold mb-2">Card with Image</h3>
<p class="text-gray-600">Image at the top</p>
</div>
</div>
<!-- Compact bordered card -->
<div class="card-bordered card-compact">
<div class="card-header">
<h3 class="font-bold">Compact Card</h3>
</div>
<div class="card-body">
<p class="text-sm text-gray-600">Less padding, with border</p>
</div>
</div>
<!-- Elevated card -->
<div class="card-elevated">
<div class="card-body">
<h3 class="text-xl font-bold mb-2">Elevated Card</h3>
<p class="text-gray-600">Extra shadow for emphasis</p>
</div>
</div>Badge Component Patterns
Create status badges and labels:
@import "tailwindcss";
@layer components {
/* Badge base */
.badge {
@apply inline-flex items-center gap-1.5 px-3 py-1;
@apply text-sm font-semibold rounded-full;
}
/* Color variants */
.badge-primary {
@apply badge bg-blue-100 text-blue-800;
@apply dark:bg-blue-900/30 dark:text-blue-400;
}
.badge-success {
@apply badge bg-green-100 text-green-800;
@apply dark:bg-green-900/30 dark:text-green-400;
}
.badge-warning {
@apply badge bg-yellow-100 text-yellow-800;
@apply dark:bg-yellow-900/30 dark:text-yellow-400;
}
.badge-danger {
@apply badge bg-red-100 text-red-800;
@apply dark:bg-red-900/30 dark:text-red-400;
}
.badge-gray {
@apply badge bg-gray-100 text-gray-800;
@apply dark:bg-gray-700 dark:text-gray-300;
}
/* Sizes */
.badge-sm {
@apply px-2 py-0.5 text-xs;
}
.badge-lg {
@apply px-4 py-1.5 text-base;
}
/* With dot indicator */
.badge-dot::before {
content: '';
@apply w-2 h-2 rounded-full bg-current;
}
}<!-- Basic badges -->
<span class="badge-primary">Primary</span>
<span class="badge-success">Success</span>
<span class="badge-warning">Warning</span>
<span class="badge-danger">Danger</span>
<span class="badge-gray">Neutral</span>
<!-- Sizes -->
<span class="badge-success badge-sm">Small</span>
<span class="badge-success">Default</span>
<span class="badge-success badge-lg">Large</span>
<!-- With dot indicator -->
<span class="badge-success badge-dot">Active</span>
<span class="badge-danger badge-dot">Offline</span>
<!-- With icon -->
<span class="badge-primary">
<svg class="w-4 h-4" fill="currentColor">...</svg>
<span>With Icon</span>
</span>
<!-- Removable badge -->
<span class="badge-primary">
<span>Removable</span>
<button class="hover:bg-blue-200 dark:hover:bg-blue-800 rounded-full p-0.5">
<svg class="w-3 h-3" fill="currentColor">...</svg>
</button>
</span>
<!-- Use in context -->
<div class="flex items-center gap-2">
<h3 class="text-lg font-bold">Project Title</h3>
<span class="badge-success badge-sm">Active</span>
</div>Alert Component Patterns
Build notification and alert components:
@import "tailwindcss";
@layer components {
/* Alert base */
.alert {
@apply flex items-start gap-3 p-4 rounded-lg;
}
/* Variants */
.alert-info {
@apply alert bg-blue-50 border-l-4 border-blue-500;
@apply dark:bg-blue-900/20 dark:border-blue-400;
}
.alert-success {
@apply alert bg-green-50 border-l-4 border-green-500;
@apply dark:bg-green-900/20 dark:border-green-400;
}
.alert-warning {
@apply alert bg-yellow-50 border-l-4 border-yellow-500;
@apply dark:bg-yellow-900/20 dark:border-yellow-400;
}
.alert-danger {
@apply alert bg-red-50 border-l-4 border-red-500;
@apply dark:bg-red-900/20 dark:border-red-400;
}
/* Alert title */
.alert-title {
@apply font-semibold text-sm mb-1;
}
.alert-info .alert-title {
@apply text-blue-800 dark:text-blue-200;
}
.alert-success .alert-title {
@apply text-green-800 dark:text-green-200;
}
.alert-warning .alert-title {
@apply text-yellow-800 dark:text-yellow-200;
}
.alert-danger .alert-title {
@apply text-red-800 dark:text-red-200;
}
/* Alert description */
.alert-description {
@apply text-sm;
}
.alert-info .alert-description {
@apply text-blue-700 dark:text-blue-300;
}
.alert-success .alert-description {
@apply text-green-700 dark:text-green-300;
}
.alert-warning .alert-description {
@apply text-yellow-700 dark:text-yellow-300;
}
.alert-danger .alert-description {
@apply text-red-700 dark:text-red-300;
}
}<!-- Info alert -->
<div class="alert-info">
<svg class="w-5 h-5 text-blue-500 dark:text-blue-400 flex-shrink-0" fill="currentColor">...</svg>
<div>
<p class="alert-title">Information</p>
<p class="alert-description">This is an informational message.</p>
</div>
</div>
<!-- Success alert -->
<div class="alert-success">
<svg class="w-5 h-5 text-green-500 dark:text-green-400 flex-shrink-0" fill="currentColor">...</svg>
<div>
<p class="alert-title">Success!</p>
<p class="alert-description">Your changes have been saved.</p>
</div>
</div>
<!-- Warning alert -->
<div class="alert-warning">
<svg class="w-5 h-5 text-yellow-500 dark:text-yellow-400 flex-shrink-0" fill="currentColor">...</svg>
<div>
<p class="alert-title">Warning</p>
<p class="alert-description">Please review before proceeding.</p>
</div>
</div>
<!-- Danger alert -->
<div class="alert-danger">
<svg class="w-5 h-5 text-red-500 dark:text-red-400 flex-shrink-0" fill="currentColor">...</svg>
<div>
<p class="alert-title">Error!</p>
<p class="alert-description">Something went wrong. Please try again.</p>
</div>
</div>
<!-- Dismissible alert -->
<div class="alert-info">
<svg class="w-5 h-5 flex-shrink-0" fill="currentColor">...</svg>
<div class="flex-1">
<p class="alert-title">Dismissible Alert</p>
<p class="alert-description">You can close this alert.</p>
</div>
<button class="text-blue-500 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300">
<svg class="w-5 h-5" fill="currentColor">...</svg>
</button>
</div>
<!-- Alert with action -->
<div class="alert-info">
<svg class="w-5 h-5 flex-shrink-0" fill="currentColor">...</svg>
<div class="flex-1">
<p class="alert-title">Update Available</p>
<p class="alert-description">A new version is available.</p>
<button class="mt-2 text-sm font-semibold text-blue-600 hover:text-blue-700 dark:text-blue-400">
Update Now →
</button>
</div>
</div>Layout Component Patterns
Create reusable layout structures:
@import "tailwindcss";
@layer components {
/* Container */
.container-custom {
@apply max-w-7xl mx-auto px-4 sm:px-6 lg:px-8;
}
/* Section wrapper */
.section {
@apply py-12 md:py-16 lg:py-20;
}
.section-sm {
@apply py-8 md:py-12;
}
.section-lg {
@apply py-16 md:py-24 lg:py-32;
}
/* Stack (vertical spacing) */
.stack {
@apply space-y-4;
}
.stack-sm {
@apply space-y-2;
}
.stack-lg {
@apply space-y-8;
}
/* Cluster (horizontal spacing) */
.cluster {
@apply flex flex-wrap gap-4;
}
.cluster-sm {
@apply flex flex-wrap gap-2;
}
.cluster-lg {
@apply flex flex-wrap gap-6;
}
}<!-- Page layout -->
<div class="min-h-screen bg-gray-50 dark:bg-gray-900">
<!-- Header -->
<header class="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
<div class="container-custom">
<div class="flex items-center justify-between h-16">
<h1 class="text-xl font-bold">Logo</h1>
<nav class="cluster-sm">
<a href="#">Home</a>
<a href="#">About</a>
<a href="#">Contact</a>
</nav>
</div>
</div>
</header>
<!-- Main content -->
<main>
<section class="section">
<div class="container-custom">
<div class="stack-lg">
<h2 class="text-4xl font-bold">Section Title</h2>
<p class="text-lg text-gray-600">Section content...</p>
</div>
</div>
</section>
</main>
<!-- Footer -->
<footer class="bg-gray-900 text-white">
<div class="container-custom section-sm">
<p class="text-center">© 2026 Company Name</p>
</div>
</footer>
</div>
<!-- Stack pattern (vertical) -->
<div class="stack">
<h3>Stacked Items</h3>
<p>Item 1</p>
<p>Item 2</p>
<p>Item 3</p>
</div>
<!-- Cluster pattern (horizontal) -->
<div class="cluster">
<button class="btn-primary">Button 1</button>
<button class="btn-secondary">Button 2</button>
<button class="btn-ghost">Button 3</button>
</div>Component Composition
Combine components for complex UIs:
<!-- Profile card composition -->
<div class="card">
<div class="card-body">
<!-- Avatar + info cluster -->
<div class="flex items-center gap-4 mb-6">
<img
src="avatar.jpg"
alt="User"
class="w-16 h-16 rounded-full"
>
<div>
<h3 class="text-xl font-bold text-gray-900 dark:text-white">
John Doe
</h3>
<span class="badge-success badge-sm badge-dot">Active</span>
</div>
</div>
<!-- Stats cluster -->
<div class="cluster justify-between mb-6">
<div class="text-center">
<p class="text-2xl font-bold text-gray-900 dark:text-white">1,234</p>
<p class="text-sm text-gray-600 dark:text-gray-400">Followers</p>
</div>
<div class="text-center">
<p class="text-2xl font-bold text-gray-900 dark:text-white">567</p>
<p class="text-sm text-gray-600 dark:text-gray-400">Following</p>
</div>
<div class="text-center">
<p class="text-2xl font-bold text-gray-900 dark:text-white">89</p>
<p class="text-sm text-gray-600 dark:text-gray-400">Posts</p>
</div>
</div>
<!-- Action buttons -->
<div class="cluster">
<button class="btn-primary flex-1">Follow</button>
<button class="btn-outline-primary flex-1">Message</button>
</div>
</div>
</div>
<!-- Product card composition -->
<div class="card-hover">
<!-- Image -->
<div class="relative">
<img
src="product.jpg"
alt="Product"
class="w-full h-64 object-cover"
>
<span class="absolute top-4 right-4 badge-danger">20% OFF</span>
</div>
<!-- Content -->
<div class="card-body">
<div class="stack">
<!-- Title + badge -->
<div class="flex items-start justify-between gap-2">
<h3 class="text-xl font-bold flex-1">Product Name</h3>
<span class="badge-success badge-sm">New</span>
</div>
<!-- Description -->
<p class="text-gray-600 dark:text-gray-300">
High-quality product with amazing features
</p>
<!-- Rating -->
<div class="flex items-center gap-1">
<div class="flex text-yellow-500">
★★★★★
</div>
<span class="text-sm text-gray-600">(4.8)</span>
</div>
<!-- Price + action -->
<div class="flex items-center justify-between pt-2">
<div>
<span class="text-2xl font-bold text-gray-900 dark:text-white">$99</span>
<span class="text-sm text-gray-500 line-through ml-2">$124</span>
</div>
<button class="btn-primary">Add to Cart</button>
</div>
</div>
</div>
</div>
<!-- Notification toast composition -->
<div class="card max-w-md shadow-2xl">
<div class="alert-success">
<svg class="w-5 h-5 flex-shrink-0" fill="currentColor">...</svg>
<div class="flex-1">
<p class="alert-title">Success!</p>
<p class="alert-description">Your profile has been updated.</p>
</div>
<button class="text-green-600 hover:text-green-700">
<svg class="w-5 h-5" fill="currentColor">...</svg>
</button>
</div>
</div>Component Patterns Playground
Practice building with component patterns:
Build Component Libraries
Practice creating reusable component patterns
Component Library
ActiveButtons
Card Title
Card description goes here...
Another Card
More content here...
Third Card
Final card content...
Information
These are reusable component patterns you can use in your projects.
Component Pattern Best Practices
Follow These Guidelines:
- Start with utilities: Build with utilities first, extract later
- Extract when repeating: Create components when patterns appear 3+ times
- Keep components focused: Single responsibility per component
- Use composition: Combine simple components for complex UIs
- Organize classes: Group by category (layout, spacing, colors)
- Document patterns: Comment complex class combinations
- Maintain flexibility: Allow utility overrides on components
- Test dark mode: Ensure components work in both themes
Key Takeaways
- Default to utility classes, extract component classes when patterns repeat
- Organize long class lists by category for better readability
- Use
@layer componentsto create reusable component classes - Build button, card, badge, and alert component systems
- Create layout utilities for consistent spacing and structure
- Compose simple components to build complex interfaces
- Use clsx/classnames for conditional class application
- Keep components flexible with utility class overrides
- Document component patterns for team consistency
- Test all components in light and dark modes
What's Next?
You've mastered component patterns! You can now build reusable, maintainable component libraries with Tailwind.
In the next lesson, we'll explore Forms & Interactive Elements—learning to style inputs, buttons, selects, checkboxes, and create beautiful, accessible forms with proper focus states and validation feedback.
🎯 Practice Challenge!
Build a complete component library! Create:
- 5 button variants (primary, secondary, outline, ghost, danger)
- 3 card patterns (basic, hoverable, with image)
- 4 badge types (success, warning, danger, info)
- Alert components (info, success, warning, error)
- Layout utilities (container, section, stack, cluster)
Organize them in a single CSS file and create a demo page showcasing all components!