Configuration & Setup (Tips 1–5)
Getting your tailwind.config.js right saves hours of rework later. These five tips cover the decisions you should make before writing a single utility class.
Tip 1 — Extend, Do Not Overwrite
Properties placed inside theme.extend are added to the defaults. Properties placed directly inside theme replace the defaults entirely. Unless you deliberately want to drop built-in values, always extend.
module.exports = {
theme: {
extend: {
colors: {
brand: '#6366f1', // Added alongside all default colors
surface: '#111118',
},
spacing: {
'18': '4.5rem', // Fills the gap between 16 (4rem) and 20 (5rem)
},
},
},
}
Tip 2 — Use CSS Variables for Theming
Define colors as CSS custom properties so you can swap entire palettes at runtime — perfect for multi-brand sites or user-customizable themes. Generate your palette quickly with the Tailwind Color Generator.
module.exports = {
theme: {
extend: {
colors: {
primary: 'rgb(var(--color-primary) / <alpha-value>)',
surface: 'rgb(var(--color-surface) / <alpha-value>)',
},
},
},
}
:root {
--color-primary: 99 102 241; /* Indigo */
--color-surface: 17 17 24;
}
.theme-emerald {
--color-primary: 16 185 129; /* Swap theme with one class */
}
Tip 3 — Set Content Paths Precisely
The content array tells Tailwind which files to scan for class names. Overly broad globs slow down compilation. Overly narrow globs cause missing styles.
module.exports = {
content: [
'./src/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx}',
'./app/**/*.{js,ts,jsx,tsx}',
// Include UI library if it uses Tailwind classes
'./node_modules/@your-org/ui/dist/**/*.js',
],
}
Never include node_modules/** without scoping to a specific package. Scanning all of node_modules can take minutes and trigger false-positive class matches.
Tip 4 — Create Design Tokens in Config
Centralize repeated values like border radii, shadows, and font sizes in config. This gives you named utilities instead of arbitrary values scattered across templates.
module.exports = {
theme: {
extend: {
borderRadius: {
'card': '12px',
'button': '8px',
'pill': '9999px',
},
boxShadow: {
'glow': '0 0 20px rgba(99, 102, 241, 0.3)',
'card': '0 4px 24px rgba(0, 0, 0, 0.12)',
},
fontSize: {
'hero': ['3.5rem', { lineHeight: '1.1', letterSpacing: '-0.03em' }],
},
},
},
}
Now you write rounded-card, shadow-glow, and text-hero instead of memorizing pixel values.
Tip 5 — Enable the Prettier Plugin
The official Prettier plugin for Tailwind CSS sorts utility classes into a consistent order on every save. No more debates about class ordering in pull requests. For quick, one-off reordering without a build step, use the Tailwind Class Sorter directly in your browser.
npm install -D prettier-plugin-tailwindcss
{
"plugins": ["prettier-plugin-tailwindcss"],
"tailwindFunctions": ["clsx", "cn", "cva"]
}
Responsive Design (Tips 6–10)
Tailwind's responsive system uses mobile-first breakpoints, which means unprefixed utilities apply to all screen sizes and prefixed utilities apply at that breakpoint and above.
Tip 6 — Build Mobile-First, Always
Write the mobile layout first with unprefixed classes, then layer on breakpoints. This produces less CSS and forces you to design for the hardest constraint first.
<!-- Mobile: single column. md: sidebar + main. lg: wider sidebar -->
<div class="flex flex-col md:flex-row gap-6">
<aside class="w-full md:w-64 lg:w-80">Sidebar</aside>
<main class="flex-1">Content</main>
</div>
Tip 7 — Add Custom Breakpoints
The default breakpoints (sm:640, md:768, lg:1024, xl:1280, 2xl:1536) work for most projects. But if you target specific devices or design specs, add custom ones.
module.exports = {
theme: {
extend: {
screens: {
'xs': '475px', // Small phones
'3xl': '1800px', // Ultra-wide monitors
'tall': { 'raw': '(min-height: 800px)' }, // Height-based
},
},
},
}
Now xs:text-sm kicks in at 475px and tall:py-12 activates when the viewport is at least 800px tall.
Tip 8 — Use Container Queries
Tailwind v3.3+ supports CSS container queries with the @container directive. Components respond to their parent's width instead of the viewport — critical for reusable widgets.
<div class="@container">
<div class="flex flex-col @md:flex-row gap-4">
<img class="w-full @md:w-48 rounded-lg" src="..." alt="...">
<div class="flex-1">
<h3 class="text-lg @lg:text-xl font-bold">Title</h3>
<p class="text-sm text-gray-500">Description</p>
</div>
</div>
</div>
Tip 9 — Responsive Hiding and Showing
Control visibility per breakpoint with hidden and block (or flex, grid) instead of toggling opacity or position.
<!-- Show mobile menu icon on small screens, nav links on md+ -->
<button class="md:hidden">Menu</button>
<nav class="hidden md:flex gap-6">
<a href="#">Home</a>
<a href="#">About</a>
<a href="#">Contact</a>
</nav>
Tip 10 — Responsive Typography with Clamp
Combine Tailwind's arbitrary value syntax with CSS clamp() for fluid font sizes that scale smoothly without breakpoints.
<h1 class="text-[clamp(2rem,5vw,4rem)] font-extrabold leading-tight">
Fluid Headline
</h1>
This heading is 2rem on small screens, scales with viewport width, and caps at 4rem on large screens. No breakpoints, no jumps.
Dark Mode (Tips 11–13)
Tip 11 — Class-Based Dark Mode
Set darkMode: 'class' in your config, then toggle a dark class on <html>. This gives you full control and lets users override system preference.
module.exports = {
darkMode: 'class',
}
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
<p class="text-gray-600 dark:text-gray-400">
This text adapts to the current mode.
</p>
</div>
Tip 12 — Smooth Dark Mode Transitions
Without a transition, toggling dark mode feels jarring. Add a global transition on background and color to make the switch feel polished.
*,
*::before,
*::after {
transition-property: background-color, border-color, color;
transition-duration: 200ms;
transition-timing-function: ease;
}
This universal transition is lightweight because background-color, border-color, and color are compositable properties. Avoid transitioning all, which can trigger layout recalculations on every animation frame.
Tip 13 — Detect System Preference with JavaScript
Respect the user's OS setting as the default, then let them override it manually. Store the override in localStorage.
// On page load, apply the right theme
const stored = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (stored === 'dark' || (!stored && prefersDark)) {
document.documentElement.classList.add('dark');
}
// Toggle function
function toggleDarkMode() {
const isDark = document.documentElement.classList.toggle('dark');
localStorage.setItem('theme', isDark ? 'dark' : 'light');
}
Layout & Spacing (Tips 14–17)
Tip 14 — Arbitrary Values for One-Off Sizes
Square bracket syntax lets you use any CSS value without touching config. Use it for values that appear once or twice — not for recurring design tokens.
<div class="w-[calc(100%-2rem)] max-w-[780px] mx-auto">
<img class="h-[clamp(200px,30vw,500px)] object-cover" src="..." alt="...">
</div>
<!-- Arbitrary colors, grids, anything -->
<div class="bg-[#0d0d14] grid-cols-[200px_1fr_200px] border-[3px]">
...
</div>
Tip 15 — Space-Y and Space-X for Stack Spacing
The space-y-* and space-x-* utilities add margin between sibling elements. They are simpler than gap for one-directional stacks that do not use Flexbox or Grid.
<div class="space-y-4">
<p>First paragraph</p>
<p>Second paragraph (margin-top: 1rem)</p>
<p>Third paragraph (margin-top: 1rem)</p>
</div>
For Flex and Grid containers, prefer gap-* instead. It avoids issues with hidden or conditionally rendered children.
Tip 16 — Divide Utilities for Borders Between Items
The divide-y and divide-x utilities add borders between child elements without adding them on the first or last item. Perfect for lists and table rows.
<ul class="divide-y divide-gray-800">
<li class="py-3">Item one</li>
<li class="py-3">Item two</li>
<li class="py-3">Item three</li>
</ul>
Tip 17 — The Size Utility
Tailwind v3.4+ includes the size-* utility that sets both width and height simultaneously. Use it for avatars, icons, and square elements.
<!-- Before: two classes -->
<div class="w-10 h-10 rounded-full bg-indigo-500"></div>
<!-- After: one class -->
<div class="size-10 rounded-full bg-indigo-500"></div>
Typography & Colors (Tips 18–20)
Tip 18 — Prose for Long-Form Content
The @tailwindcss/typography plugin provides the prose class that styles raw HTML content (from a CMS or Markdown). It handles headings, paragraphs, lists, code blocks, blockquotes, and tables with sensible defaults.
npm install -D @tailwindcss/typography
<article class="prose prose-invert prose-indigo lg:prose-lg max-w-none">
<!-- Markdown-rendered HTML goes here -->
<h2>Styled automatically</h2>
<p>No extra classes needed on inner elements.</p>
</article>
The prose-invert modifier flips colors for dark backgrounds. prose-indigo sets the link color to your brand.
Tip 19 — Gradient Text
Create eye-catching gradient headings with three Tailwind utilities. No custom CSS required.
<h1 class="text-5xl font-extrabold
bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500
bg-clip-text text-transparent">
Gradient Heading
</h1>
The combination of bg-clip-text and text-transparent clips the gradient to the text shape. This works in all modern browsers and degrades gracefully in older ones.
Tip 20 — Custom Font Stacks
Define your full font stack in config so every font-* class uses your chosen typeface with proper fallbacks.
const defaultTheme = require('tailwindcss/defaultTheme');
module.exports = {
theme: {
extend: {
fontFamily: {
sans: ['Inter', ...defaultTheme.fontFamily.sans],
mono: ['JetBrains Mono', ...defaultTheme.fontFamily.mono],
display: ['Cal Sans', 'Inter', ...defaultTheme.fontFamily.sans],
},
},
},
}
Now font-sans uses Inter with proper system fallbacks, font-mono uses JetBrains Mono, and font-display is available for hero sections.
Animations & Transitions (Tips 21–23)
Tip 21 — Built-In Animation Utilities
Tailwind ships with four animations out of the box: animate-spin, animate-ping, animate-pulse, and animate-bounce. Use them for loading indicators, notification badges, and attention-grabbing CTAs.
<!-- Loading spinner -->
<svg class="animate-spin size-5 text-white" viewBox="0 0 24 24">...</svg>
<!-- Notification dot -->
<span class="relative flex size-3">
<span class="animate-ping absolute inline-flex size-full rounded-full
bg-indigo-400 opacity-75"></span>
<span class="relative inline-flex size-3 rounded-full bg-indigo-500"></span>
</span>
<!-- Skeleton loader -->
<div class="animate-pulse space-y-3">
<div class="h-4 bg-gray-700 rounded w-3/4"></div>
<div class="h-4 bg-gray-700 rounded w-1/2"></div>
</div>
Tip 22 — Custom Keyframe Animations
Define custom animations in config when the built-in four are not enough. This keeps animations in the same system as all your other design tokens.
module.exports = {
theme: {
extend: {
keyframes: {
'fade-in': {
'0%': { opacity: '0', transform: 'translateY(8px)' },
'100%': { opacity: '1', transform: 'translateY(0)' },
},
'slide-in-right': {
'0%': { transform: 'translateX(100%)' },
'100%': { transform: 'translateX(0)' },
},
},
animation: {
'fade-in': 'fade-in 0.5s ease-out forwards',
'slide-in-right': 'slide-in-right 0.3s ease-out',
},
},
},
}
<div class="animate-fade-in">Appears with a soft fade</div>
<div class="animate-slide-in-right">Slides from the right</div>
Tip 23 — Transition Shorthands
Tailwind provides transition, duration-*, ease-*, and delay-* for common interactive transitions. Combine them for buttons, cards, and hover effects.
<button class="bg-indigo-600 hover:bg-indigo-500
transition-colors duration-200 ease-out
hover:-translate-y-0.5 hover:shadow-lg
transition-all">
Hover Me
</button>
<!-- Card with lift effect -->
<div class="rounded-xl border border-gray-800 p-6
transition-all duration-300
hover:border-indigo-500 hover:-translate-y-1
hover:shadow-[0_8px_30px_rgba(99,102,241,0.15)]">
Card Content
</div>
Prefer transition-colors or transition-shadow over transition-all when you only animate one property. Narrower transition scopes are cheaper for the browser to compute.
Advanced Patterns (Tips 24–25)
Tip 24 — Group and Peer Modifiers
The group modifier styles a child based on a parent's state. The peer modifier styles a sibling based on another sibling's state. Together they handle most interactive patterns without JavaScript.
<!-- Group: child reacts to parent hover -->
<a href="#" class="group flex items-center gap-3 p-4 rounded-lg
hover:bg-gray-800 transition-colors">
<div class="size-10 rounded-full bg-gray-700
group-hover:bg-indigo-600 transition-colors"></div>
<span class="text-gray-400 group-hover:text-white transition-colors">
Profile
</span>
</a>
<!-- Peer: label reacts to input state -->
<input type="email" class="peer border rounded px-3 py-2
invalid:border-red-500" placeholder="Email">
<p class="hidden peer-invalid:block text-red-500 text-sm mt-1">
Please enter a valid email address.
</p>
Named groups and peers (group/sidebar, peer/email) let you nest multiple group contexts without conflicts.
Tip 25 — Write a Custom Plugin
When you need utilities that Tailwind does not provide, write a plugin. Plugins have full access to Tailwind's API and integrate seamlessly with variants, responsive prefixes, and dark mode. If you need to convert existing CSS to Tailwind syntax before extracting it into a plugin, the Tailwind Converter can speed up the process.
const plugin = require('tailwindcss/plugin');
module.exports = {
plugins: [
plugin(function({ addUtilities, theme }) {
addUtilities({
'.text-balance': {
'text-wrap': 'balance',
},
'.text-pretty': {
'text-wrap': 'pretty',
},
'.scrollbar-hide': {
'-ms-overflow-style': 'none',
'scrollbar-width': 'none',
'&::-webkit-scrollbar': {
display: 'none',
},
},
'.glass': {
'background': 'rgba(17, 17, 24, 0.6)',
'backdrop-filter': 'blur(12px)',
'-webkit-backdrop-filter': 'blur(12px)',
'border': `1px solid ${theme('colors.gray.800')}`,
},
});
}),
],
}
Now glass, text-balance, text-pretty, and scrollbar-hide work like native Tailwind utilities, including with hover:, md:, dark:, and every other variant.
Tailwind CSS Tools for Your Workflow
Sort classes, convert CSS to Tailwind syntax, and generate custom color palettes — all free, all in your browser.
Class Sorter CSS-to-Tailwind ConverterTailwind Tools
Speeding up your Tailwind workflow often comes down to having the right tools open alongside your editor. These three cover the most common needs.
Frequently Asked Questions
Properties placed inside the theme.extend object are added to the default Tailwind values, so you keep all built-in utilities. Properties placed directly inside theme (outside extend) replace the defaults entirely. For example, adding colors inside extend gives you your custom colors plus every default color. Adding them outside extend removes all default colors and only keeps yours. The safest approach is to always use extend unless you intentionally want to remove defaults.
Add darkMode: 'class' to your tailwind.config.js file. Then use the dark: prefix on any utility class, such as dark:bg-gray-900 or dark:text-white. Toggle dark mode by adding or removing the 'dark' class on the html or body element with JavaScript. You can also use darkMode: 'media' to follow the operating system preference automatically, but the class strategy gives you manual control and works better with user preference toggles.
Yes. Tailwind supports arbitrary values using square bracket syntax. Write any CSS value directly in your class names, like w-[137px], bg-[#1a1a2e], grid-cols-[200px_1fr_200px], or text-[clamp(1rem,3vw,2rem)]. Arbitrary values work with nearly every utility and are processed by the JIT compiler on the fly. They are useful for one-off values that do not justify a config entry.
Follow a consistent ordering convention: layout (display, position, overflow), box model (width, height, margin, padding), typography (font, text, leading), visual (background, border, shadow, opacity), and interactive (hover, focus, transition). Use the official Prettier plugin for Tailwind CSS to automatically sort classes on save. For repeated patterns, extract components or use @apply in a CSS file to reduce duplication. Tools like the NexTool Tailwind Class Sorter can also reorder classes for you instantly.
Yes. Tailwind's build process scans your source files and only includes the CSS classes you actually use. A typical production build generates 8-15 KB of gzipped CSS, which is smaller than most component libraries. The JIT engine compiles on demand so the development experience is fast, and tree-shaking is automatic. Combined with proper caching headers, Tailwind sites load as fast or faster than hand-written CSS in most real-world scenarios.