· 5 min read

Top 5 Mistakes Newcomers Make with Tailwind CSS (And How to Avoid Them)

A practical guide for developers new to Tailwind CSS: five common mistakes, why they happen, and clear, copy-paste solutions to fix them fast - including config tips, component extraction patterns, build optimizations, and accessibility reminders.

Introduction

Tailwind CSS is incredibly powerful - but its utility-first approach can trip up newcomers. Below are the top 5 mistakes I see time and again, why they cause problems, and concrete fixes you can apply right away.

Mistake 1 - Treating Tailwind as “inline styles” instead of a design system

Why it happens

  • Newcomers often write large, page-specific utility lists (a.k.a. very long class attributes) for every element.
  • This works at first but quickly becomes repetitive and brittle.

What goes wrong

  • Duplicate logic across many elements.
  • Harder to maintain and refactor.
  • Hard to keep design consistent.

How to avoid it

  • Extract shared patterns into named components using one of these approaches:
    • CSS components with @apply
    • Reusable UI components (React/Vue/Svelte) that take props
    • Utility extraction tools (e.g., using a small wrapper or a class-name helper)

Examples

  1. Using @apply in a stylesheet to create a reusable class (recommended for non-dynamic variants):
/* src/styles/components.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer components {
  .btn {
    @apply inline-flex items-center px-4 py-2 rounded-md text-sm font-medium;
  }
  .btn-primary {
    @apply btn bg-blue-600 text-white hover:bg-blue-700;
  }
}

Then in markup:

<button class="btn-primary">Save</button>
  1. Extracting into a React component with clsx (for conditional classes):
import clsx from 'clsx';

export default function Button({
  children,
  variant = 'primary',
  className,
  ...props
}) {
  const base =
    'inline-flex items-center px-4 py-2 rounded-md text-sm font-medium';
  const variants = {
    primary: 'bg-blue-600 text-white hover:bg-blue-700',
    ghost: 'bg-transparent text-gray-800 hover:bg-gray-100',
  };

  return (
    <button className={clsx(base, variants[variant], className)} {...props}>
      {children}
    </button>
  );
}

Resources: see the Tailwind docs on @apply.

Mistake 2 - Not configuring Tailwind’s theme and relying only on utility defaults

Why it happens

  • Beginners copy-paste utilities and don’t customize the design tokens in tailwind.config.js.

What goes wrong

  • Inconsistent colors, spacing, typography across the project.
  • Repetitive use of arbitrary values which undermines design system benefits.

How to avoid it

  • Create a small, opinionated tailwind.config.js early. Add your brand colors, spacing scale, and font stacks.
  • Use the theme tokens throughout components so changes are global and predictable.

Example minimal config

// tailwind.config.js
module.exports = {
  content: ['./src/**/*.{js,jsx,ts,tsx,html}'],
  theme: {
    extend: {
      colors: {
        brand: {
          50: '#f5f9ff',
          500: '#2563eb',
          700: '#1e40af',
        },
      },
      spacing: {
        72: '18rem',
      },
    },
  },
  plugins: [],
};

Tip: use semantic names when appropriate (e.g., btn, card) on top of color tokens.

Docs: Configuration.

Mistake 3 - Misunderstanding build and purge (content) settings - huge CSS or missing classes

Why it happens

  • New users copy config without setting the content (formerly purge) paths or confuse dev vs production builds.
  • With dynamic classes (e.g., constructed class strings), the extractor may miss classes causing them to be removed in production.

What goes wrong

  • Production CSS remains huge (if purge is disabled) or necessary utility classes are missing (if the extractor can’t see them).

How to avoid it

  • Always set the content array to include every file that contains Tailwind classes (JSX, Vue, Svelte, HTML, etc.).
  • For dynamic classes, use safe patterns so Tailwind can detect them, or whitelist classes. Prefer stable patterns or CSS/JS component extraction.
  • Use the recommended production build step (Tailwind automatically removes unused styles when used with a proper bundler and the content config).

Example content config for a Next.js app

// tailwind.config.js
module.exports = {
  content: [
    './pages/**/*.{js,ts,jsx,tsx}',
    './components/**/*.{js,ts,jsx,tsx}',
    './app/**/*.{js,ts,jsx,tsx}',
  ],
  theme: {
    /* ... */
  },
};

If you build class names dynamically, prefer mapping objects or small helper functions:

// BAD: `bg-${color}-500` can't be detected by Tailwind's static extractor
const color = 'blue';
<div className={`bg-${color}-500`}></div>;

// BETTER: map values explicitly
const bgMap = { blue: 'bg-blue-500', red: 'bg-red-500' };
<div className={bgMap[color]}></div>;

Docs: Optimizing for production

Mistake 4 - Ignoring mobile-first responsive mindset and variant ordering

Why it happens

  • Coming from Bootstrap or another framework, newcomers sometimes assume Tailwind’s breakpoint utilities are applied differently.

What goes wrong

  • Unexpected layout on small screens because they wrote styles for large screens first or misused breakpoints.

How to avoid it

  • Tailwind is mobile-first: base utilities apply to all sizes; prefixed utilities (sm:, md:, lg:, xl:, 2xl:) apply at that breakpoint and up.
  • Write base (mobile) styles first, then layer breakpoints for larger screens.

Good example

<!-- Mobile-first -->
<div class="p-3 text-sm md:p-6 md:text-base lg:p-8">Content</div>

Bad pattern (hard to reason about):

<!-- Don't start with large styles then try to override them for small screens -->
<div class="lg:p-8 p-3"></div>

Also be aware that ordering of breakpoint utilities in the class attribute does not matter - Tailwind generates the CSS by breakpoint - but your mental model should be mobile-first.

Docs: Responsive design

Mistake 5 - Forgetting semantics and accessibility because utilities make layout easy

Why it happens

  • It’s tempting to use divs everywhere and style them with utilities.

What goes wrong

  • Poor accessibility, worse SEO, and brittle UX for keyboard / screen-reader users.

How to avoid it

  • Always choose semantic elements where appropriate: use
  • Use ARIA attributes when semantics need augmentation, not as a replacement for semantic markup.
  • Make focus states visible (Tailwind includes focus utilities) and test keyboard navigation.

Examples

<!-- Semantic button with visible focus ring -->
<button class="btn-primary focus:outline-none focus:ring-4 focus:ring-blue-200">
  Save
</button>

<!-- Use proper headings -->
<main>
  <h1 class="text-2xl font-bold">Page title</h1>
  <section aria-labelledby="features-heading">
    <h2 id="features-heading">Features</h2>
    <!-- ... -->
  </section>
</main>

Helpful extensions and tools

Quick checklist before shipping

  • Is tailwind.config.js configured (content + theme)?
  • Are repeated utility lists extracted into components or @apply classes?
  • Do CSS builds remove unused utilities (production optimized)?
  • Does the layout follow mobile-first breakpoints?
  • Are semantic HTML and accessibility concerns addressed?
  • Are dynamic classes expressed in a way Tailwind can detect them?

Conclusion

Tailwind gives you speed and consistency when you embrace its system mindset: configure tokens, extract components, think mobile-first, and never forget semantics. Avoid these common rookie mistakes and you’ll get the real benefits of increased productivity and maintainable UI code.

References

Back to Blog

Related Posts

View All Posts »

The Future of Tailwind CSS: Predictions for 2024 and Beyond

A forward-looking analysis of how trends in web design and browser capabilities - from container queries and cascade layers to AI-driven tooling and design tokens - will shape Tailwind CSS and the wider utility-first movement in 2024 and beyond.