Topics in this subject
Tailwind 3 min read Updated 12 Aug 2026

Gotchas and Antipatterns

Common mistakes like dynamic class construction and specificity issues.

🧑‍🏫 Sabse pehle — simple mein samjho#

Jaise har powerful tool ke kuch khatre hote hain, waise hi Tailwind mein bhi log kuch common galtiyan karte hain. Sabse badi galti hai classes ko JS ke variables jod kar banana (jaise bg-${color}-500). Isse Tailwind ko pata hi nahi chalta ki wo class exist karti hai ya nahi, aur wo usko final CSS mein add hi nahi karta. Phir log sochte hain ki unka color kyu nahi aa raha! Aisi hi 2-3 cheezein hain jinka dhyan rakhna bahot zaroori hai.

1. Dynamic Class Construction (The #1 Mistake ❌)#

Tailwind extracts classes by scanning your source code for unbroken strings. It does not execute JavaScript at build time. If you dynamically concatenate a class name, Tailwind will miss it entirely, and it won't be included in your final CSS bundle.

Antipattern ❌:

// Tailwind scanning engine cannot read this!
function Button({ color }) {
  return <button className={`bg-${color}-500 text-white p-2`}>Click</button>;
}
// Usage: <Button color="red" /> -> Renders without a background!

The Right Way ✅ (Complete Strings): Always use full, unbroken class strings.

const colorVariants = {
  blue: 'bg-blue-500 hover:bg-blue-600',
  red: 'bg-red-500 hover:bg-red-600',
};

function Button({ color }) {
  return <button className={`${colorVariants[color]} text-white p-2`}>Click</button>;
}

2. Using @apply for Everything (The Specificity Nightmare)#

As mentioned in the previous section, treating @apply as a way to write BEM CSS defeats the entire purpose of the framework. However, there's a technical reason to avoid it too: CSS Specificity.

When you use @apply, the generated CSS class has higher specificity than Tailwind's base utilities. If you write .btn { @apply bg-blue-500; } and then try to override it in HTML like <button class="btn bg-red-500">, the background might stubbornly remain blue depending on the order of execution!

The Fix: Rely on React/Vue components to group utilities instead of extracting them to CSS.

3. Specificity Clashes with className Props#

When you build a reusable component and accept a className prop, simply concatenating them together can lead to unpredictable styling because CSS relies on the order in which rules are defined, not the order in the HTML string.

The Problem ❌:

function Card({ className }) {
  // If className="p-8", which padding wins? p-4 or p-8? It's a gamble!
  return <div className={`p-4 bg-white ${className}`}>Content</div>;
}

The Fix ✅ (Use tailwind-merge): In the React ecosystem, the industry standard is to use a tiny utility library called tailwind-merge (often combined with clsx). It smartly understands Tailwind classes and resolves conflicts correctly, ensuring the trailing class always wins.

import { twMerge } from "tailwind-merge";

function Card({ className }) {
  return <div className={twMerge("p-4 bg-white", className)}>Content</div>;
}

4. Unnecessary Responsive Overrides#

Due to Tailwind's mobile-first nature, beginners often over-specify constraints.

Antipattern ❌:

<div class="w-full sm:w-full md:w-full lg:w-1/2">

You don't need to repeat w-full. The base utility acts as the default until a breakpoint overrides it.

The Right Way ✅:

<!-- The Right Way: You don't need w-full sm:w-full md:w-full! -->
<!-- Try changing lg:w-1/2 to lg:w-1/3 and resize the browser! -->
<div class="flex flex-col items-center p-8 bg-gray-50 rounded-2xl border border-gray-200">
  <div class="w-full lg:w-1/2 bg-white p-6 rounded-xl shadow-lg border border-gray-100 text-center transition-all">
    <h3 class="font-bold text-gray-800 m-0 text-xl">Responsive Card</h3>
    <p class="text-gray-500 mt-2 mb-0">
      I take up 100% width on mobile and tablets, but only 50% width on large screens (laptops)!
    </p>
  </div>
</div>