Handling Tailwind CSS in Next.js: Common Issues and Practical Solutions

This article explains how to set up Tailwind CSS in a Next.js project, addresses dynamic class name problems, class‑priority conflicts, and provides solutions using tailwind‑merge, clsx, and class‑variance‑authority, along with useful tools and VSCode extensions for smoother development.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Handling Tailwind CSS in Next.js: Common Issues and Practical Solutions

Introduction

Tailwind CSS has become a mainstream CSS framework for front‑end development, with over 80k GitHub stars and millions of weekly npm downloads. Next.js scaffolding includes Tailwind by default, allowing developers to start using it immediately. Although Tailwind appears simple, there are hidden pitfalls that this guide will cover, followed by a list of helpful websites and tools.

Project Initialization

Start a fresh Next.js project: npx create-next-app@latest When prompted, enable Tailwind CSS and App Router. Other options can be left at default.

Problem 1: Dynamic Class Names

1. Reproduction

Modify app/page.js to use a static class name:

'use client'
export default function Home() {
  return (
    <button type="submit" className="bg-indigo-600 disabled:bg-gray-500 py-2 px-4 rounded text-white w-1/2 m-2">
      Submit
    </button>
  );
}

The button renders correctly. Changing the class name to a template literal that depends on a state variable:

'use client'
import { useState } from "react";
export default function Home() {
  const [color, setColor] = useState("indigo");
  return (
    <button type="submit" className={`bg-${color}-600 disabled:bg-gray-500 py-2 px-4 rounded text-white w-1/2 m-2`}>
      Submit
    </button>
  );
}

Even after restarting the dev server, the button appears blank because Tailwind does not generate the bg-indigo-600 utility when the class name is built dynamically.

2. Explanation

Tailwind scans source files for class names using a regular expression. Any string that looks like a class name, even if it is not inside a className attribute, will be considered. Therefore, putting the class name in a variable or in text still makes Tailwind include it, but building the name at runtime prevents detection.

3. Solution

Do not construct Tailwind classes dynamically. Either list all possible classes explicitly, use a safelist in tailwind.config.js, or use a blocklist to exclude unwanted classes.

// tailwind.config.js
module.exports = {
  content: ['./pages/**/*.{html,js}', './components/**/*.{html,js}'],
  safelist: ['bg-indigo-600'],
  blocklist: ['container', 'collapse'],
};

Problem 2: Class‑Priority Conflict

1. Reproduction

Component Button defines a base red background, while the parent passes a blue background via className:

function Button({ className }) {
  return (
    <button type="submit" className={`bg-red-600 w-1/2 p-2 m-2 rounded text-white ${className}`}>
      Submit
    </button>
  );
}
export default function Home() {
  return <Button className="bg-blue-600" />;
}

The rendered button is red because Tailwind’s generated CSS order gives higher precedence to the earlier class.

2. Explanation

Class order in the HTML markup does not affect specificity; the order in the compiled CSS does. Tailwind cannot guarantee the desired order when classes are merged.

3. Solutions

3.1 tailwind‑merge

Install tailwind-merge and wrap class names with twMerge() so later arguments win:

npm i tailwind-merge
import { twMerge } from 'tailwind-merge';
function Button({ className }) {
  return (
    <button type="submit" className={twMerge('bg-red-600 w-1/2 p-2 m-2 rounded text-white', className)}>
      Submit
    </button>
  );
}

3.2 clsx

Combine clsx with twMerge to handle conditional classes:

npm i clsx
import { clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
function cn(...inputs) { return twMerge(clsx(inputs)); }

Now you can write:

className={cn('bg-red-600 w-1/2 p-2 m-2 rounded text-white', className, { 'bg-amber-600': submitting })}

3.3 cva (Class Variance Authority)

For complex component variants, install class-variance-authority and define a variant schema:

npm i class-variance-authority
import { cva } from 'class-variance-authority';
const button = cva('rounded p-2', {
  variants: {
    intent: {
      default: ['bg-blue-600', 'text-white'],
      primary: ['border', 'border-black', 'bg-white', 'text-black'],
      dashed: ['border', 'border-dashed', 'border-black', 'bg-white'],
      link: ['text-blue-600'],
      text: ['text-black'],
    },
    size: {
      small: ['px-2', 'py-2'],
      middle: ['px-4', 'py-2'],
      large: ['px-6', 'py-2'],
    },
  },
  defaultVariants: { intent: 'default', size: 'middle' },
});
function Button({ type, size }) {
  return <button className={button({ intent: type, size })}>Default Button</button>;
}

Useful Websites and Tools

1. Reference Sites

https://tailwindcomponents.com/cheatsheet/

https://tailwind.spacet.me/

CSS‑to‑Tailwind converter: https://www.divmagic.com/zh-CN/tools/css-to-tailwind

2. VSCode Extensions

Tailwind CSS IntelliSense – autocomplete, linting, and preview.

Tailwind Documentation / Tailwind Docs – quick access to official docs.

Tailwind Fold – collapses long class lists for readability.

Prettier Plugin for TailwindCSS – automatically sorts classes.

npm install -D prettier prettier-plugin-tailwindcss
{
  "plugins": ["prettier-plugin-tailwindcss"]
}

References

https://tailwindcss.com/docs/content-configuration#class-detection-in-depth

https://tailwindcss.com/blog/automatic-class-sorting-with-prettier#how-classes-are-sorted

https://www.youtube.com/watch?v=re2JFITR7TI

https://www.youtube.com/watch?v=guh9qzxkb1o&ab_channel=ByteGrad

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

frontendReactNext.jsTailwind CSSclsxcvatailwind-merge
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.