Warming up the neural circuits...
Components are reusable building blocks that encapsulate a set of utility classes. Instead of writing the same 10 classes every time you need a , you create a "Button" component once and reuse it.
Don't Repeat Yourself. If you're writing the same set of utilities more than twice, it's time to extract a component.
Use @apply in your file to group utilities into a single class:
/* styles.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.btn-primary {
@apply bg-blue-600 text-white font-semibold px-6 py-2 rounded-lg
hover:bg-blue-700 transition shadow-sm;
}
.card {
@apply bg-white rounded-xl shadow-md overflow-hidden hover:shadow-lg transition;
}
.badge {
@apply inline-flex items-center px-3 py-1 rounded-full text-sm font-medium;
}
}Then use them in :
<button class="btn-primary">Click Me</button>
<div class="card">Card content</div>
<span class="badge bg-blue-100 text-blue-800">New</span>Pros: Works with plain HTML. No framework needed.
Cons: You maintain a CSS file again. Loses the "see styling in HTML" benefit.
Create base utility patterns and modify with additional classes:
<!-- Define the pattern in your head as a convention -->
<!-- btn = bg-blue-600 text-white px-6 py-2 rounded-lg font-semibold hover:bg-blue-700 transition -->
<!-- Use consistently -->
<button class="bg-blue-600 text-white px-6 py-2 rounded-lg font-semibold hover:bg-blue-700 transition">
Primary
</button>
<button class="bg-green-600 text-white px-6 py-2 rounded-lg font-semibold hover:bg-green-700 transition"
Pros: Simple, no build tools.
Cons: Manual work, error-prone for large teams.
The recommended approach for real projects. Create a component once, pass to customize:
// Button.jsx
function Button({ children, variant = 'primary', size = 'md', className = '', ...props }) {
const base = 'inline-flex items-center justify-center font-semibold rounded-lg transition';
const
Pros: Type-safe, customizable, easy to maintain, team-friendly.
Cons: Requires a component framework.
Here's a complete set of reusable components you'd build for a typical project:
// Button variants: primary, secondary, outline, ghost, danger
// Button sizes: sm, md, lg
// Button states: disabled, loading
<Button variant="primary" size="md" disabled={isSubmitting}>
{isSubmitting ? 'Saving...' : '
function Card({ children, className = '', hover = true }) {
return (
<div className={`
bg-white rounded-xl shadow-md overflow-hidden
${hover ? 'hover:shadow-lg transition' : ''}
${className}
`}
const badgeColors = {
success: 'bg-green-100 text-green-800',
warning: 'bg-yellow-100 text-yellow-800',
error: 'bg-red-100 text-red-800',
info: 'bg-blue-100 text-blue-800',
neutral: 'bg-gray-100 text-gray-800',
};
function Badge({ children,
function Input({ label, error, className = '', ...props }) {
return (
<div className="mb-4">
{label && (
<label className="block text-sm font-medium text-gray-700 mb-1">
Each component should do one thing well. A Button handles clicks. A Card displays content. An collects data.
// ❌ Bad: Multiple similar components
<PrimaryButton />
<SecondaryButton />
<DangerButton />
// ✅ Good: One component with props
<Button variant="primary" />
<Button variant="secondary" />
<Button// ✅ Good: Compose smaller components
<Card>
<CardImage src="..." alt="..." />
<CardBody>
<CardTitle>Title</CardTitle>
<CardText>Text</CardText
// Default to most common variant
<Button>Submit</Button> // Default: primary
<Button variant="outline">Cancel</Button> // Explicit| Frequency | Action |
|---|---|
| Used once | Inline utilities (don't componentize) |
| Used 2-3 times | Maybe componentize |
| Used 4+ times | Definitely componentize |
| Part of design system | Componentize immediately |
| Complex logic (loading, error, empty states) | Componentize |
@apply, copy-paste conventions, framework componentsdark: variants to your components