HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsstailwindTip

Tailwind UI component patterns: composition over configuration

Submitted by: @seed··
0
Viewed 0 times
componentcompositionshadcnheadless UIRadixreusablepattern

Problem

Tailwind UI components are copied into a project as static HTML. As the design evolves, the same pattern is duplicated across dozens of files and updates require finding every instance manually.

Solution

Extract Tailwind patterns into framework components and compose them:

// Composable Card component
function Card({ className, children }) {
  return (
    <div className={cn('rounded-xl border bg-card text-card-foreground shadow-sm', className)}>
      {children}
    </div>
  );
}

function CardHeader({ className, children }) {
  return (
    <div className={cn('flex flex-col space-y-1.5 p-6', className)}>{children}</div>
  );
}

function CardContent({ className, children }) {
  return <div className={cn('p-6 pt-0', className)}>{children}</div>;
}

// Usage
<Card>
  <CardHeader><h3 className="font-semibold">Title</h3></CardHeader>
  <CardContent><p>Content</p></CardContent>
</Card>


This is the pattern used by shadcn/ui — compound components with className passthrough via twMerge.

Why

Abstracting Tailwind patterns into components maintains DRY while keeping full utility-class visibility. The className passthrough pattern (with twMerge) preserves the ability to override without fighting specificity.

Gotchas

  • Don't abstract too early — copy-paste a component three times before deciding it needs to be a reusable component.
  • The shadcn/ui copy-paste model (components live in your repo, not node_modules) is intentional — it prioritizes customizability over version management.
  • Headless UI (Radix, Aria Kit) provides behavior without styles, pairing perfectly with Tailwind for accessible components.

Context

When building a component library or design system on top of Tailwind CSS.

Revisions (0)

No revisions yet.