Frontend Development 15 min read

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 (
Submit
);
}

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 (
Submit
);
}

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 (
Submit
);
}
export default function Home() {
  return
;
}

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 (
Submit
);
}

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
Default 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

reactNext.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

login 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.