EthicsComputerCourses
Join training +91 90532 10052

Organising Classes & the Prettier Plugin

Tailwind CSS · Lesson 28 of 38 Advanced 8 min read Updated

Open any real Tailwind project and you will find elements with fifteen or twenty classes. If every developer writes them in a different order, code review becomes slow: is px-4 in this list or not? Is there a second, conflicting padding class at the end? A team needs one agreed order and a tool that applies it automatically.

In this lesson you will learn that class order does not change the styles, set up the official Prettier plugin that sorts classes for you (including the setting Tailwind 4 needs), sort classes inside helper functions such as clsx, clean up old or unusual class names with the CLI's canonicalize command, and decide when a long list should become a component.

Order in the attribute does not change the styles

Beginners often think the last class in the attribute wins. It does not. Which rule wins depends on the order of the rules in the CSS file that Tailwind generates, and that order is fixed. Run this example:

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Order does not matter</title>
  <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4.3.3"></script>
</head>
<body class="flex gap-4 p-6">
  <div class="bg-sky-100 p-2 p-8">p-2 p-8</div>
  <div class="bg-sky-100 p-8 p-2">p-8 p-2</div>
  <p id="out" class="self-center text-sm text-slate-600"></p>
  <script>
    setTimeout(() => {
      const [a, b] = document.querySelectorAll('div');
      document.getElementById('out').textContent =
        `First box: ${getComputedStyle(a).padding}, second box: ${getComputedStyle(b).padding}`;
    }, 300);
  </script>
</body>
</html>

Both boxes get the same padding, because p-8 comes after p-2 in the generated CSS. So sorting classes is safe: it changes how the code reads, never how the page looks. (Having two padding classes on one element is a bug of its own, covered in Debugging Class Conflicts & tailwind-merge.)

The official Prettier plugin

Prettier is a code formatter: it rewrites your files into one consistent style when you save. Tailwind Labs publishes prettier-plugin-tailwindcss, which also sorts Tailwind classes. Install both as development dependencies:

Terminal
npm install -D prettier prettier-plugin-tailwindcss

Then create .prettierrc in the project folder. With Tailwind 4, the plugin must know your main CSS file, so it can see your theme and custom utilities; paths are relative to the Prettier config file:

JSON
{
  "plugins": ["prettier-plugin-tailwindcss"],
  "tailwindStylesheet": "./src/app.css",
  "tailwindFunctions": ["clsx", "cn"]
}

We ran Prettier 3.9 with plugin 0.8.1 on this button, where src/app.css defines a brand colour and a content-auto utility:

HTML
<button class="text-white hover:bg-sky-700 px-4 md:px-6 font-semibold bg-brand rounded-lg py-2 focus-visible:outline-2 content-auto flex  shadow-sm  px-4">Pay ₹1,299</button>
Output
<button
  class="flex rounded-lg bg-brand px-4 py-2 font-semibold text-white shadow-sm content-auto hover:bg-sky-700 focus-visible:outline-2 md:px-6"
>
  Pay ₹1,299
</button>

Three things happened. The classes were sorted, the duplicate px-4 was removed, and the extra spaces were cleaned up. Your custom content-auto utility was placed among the other utilities because the plugin read app.css.

How the order works

The plugin sorts classes in the same order as the rules appear in the generated CSS:

  • Classes that are not Tailwind classes, such as card or js-toggle, go first.
  • Then plain utilities, roughly from layout (flex, grid) to box model (p-4) to typography and colour, to effects (shadow-sm).
  • Then classes with variants: states like hover: before breakpoints like sm: and lg:, so responsive changes read from small to large.

Here is real output for a mixed list: card js-toggle grid bg-white p-4 hover:shadow-md sm:p-6 lg:p-8 dark:bg-slate-900. Because the order matches the CSS, a reader can scan from left to right and know that anything further right can override something earlier.

Sorting inside functions and other attributes

By default the plugin sorts class, React's className, Vue's :class, Angular's [ngClass] and @apply lines. The tailwindFunctions setting adds function calls, which is why clsx and cn are listed above. This JSX:

React JSX
<span className={cn("text-xs px-2 rounded-full font-medium py-0.5", active && "text-white bg-brand")}>New</span>

was formatted as:

Output
<span
  className={cn(
    "rounded-full px-2 py-0.5 text-xs font-medium",
    active && "bg-brand text-white",
  )}
>
  New
</span>

For custom attributes, such as a wrapperClass prop, add them to tailwindAttributes. In template languages where the plugin cannot tell classes from template code, such as some Blade files, "tailwindPreserveDuplicates": true stops it from removing what looks like a duplicate.

Canonical class names with the CLI

Tailwind often accepts several spellings for the same style. Old code is full of them: ml-auto mr-auto instead of mx-auto, h-[100vh] instead of h-screen, v3 names such as flex-shrink-0. The Tailwind 4.3 CLI has a canonicalize command that rewrites a list into the preferred form and sorts it. A real run:

Terminal
npx @tailwindcss/cli canonicalize "mr-auto ml-auto !font-bold bg-[#fff] h-[100vh] flex-shrink-0 bg-gradient-to-r from-sky-500 px-3 py-3 mt-[0.5rem]"
Output
mx-auto mt-2 h-screen shrink-0 bg-white bg-linear-to-r from-sky-500 p-3 font-bold!

Every class was replaced by its standard name: arbitrary values that match the scale became scale classes, px-3 py-3 merged into p-3, the v3 names became v4 names, and the important marker moved to the end. Use it on a few lists during a clean-up and review each result; when a list contains two classes that fight over the same property, fix that conflict by hand first, because merging cannot know which one you meant.

Keeping long lists readable

Sorting helps, but some lists are simply long. A few habits keep them manageable:

  • Let Prettier wrap the line. A long class attribute on its own line is fine; do not add your own line breaks inside it.
  • Split by condition, not by topic. In JSX, pass the fixed classes as one string and each conditional group as another to cn() or clsx(), as in the example above.
  • Extract a component when you copy. If the same list appears in three places, make a component or template partial; see Reusable Components.
  • Use @apply only for HTML you cannot change, such as CMS output.

Without a framework you can still name groups in a small helper. This is plain JavaScript; each array holds one condition's classes:

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Grouped class lists</title>
  <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4.3.3"></script>
</head>
<body class="p-6">
  <ul id="trains" class="max-w-md space-y-2"></ul>
  <script>
    const cx = (...parts) => parts.filter(Boolean).join(' ');
    const trains = [
      { name: 'Vande Bharat, Delhi to Varanasi', late: false },
      { name: 'Kerala Express, Delhi to Kochi', late: true },
    ];
    const list = document.getElementById('trains');
    for (const train of trains) {
      const item = document.createElement('li');
      item.className = cx(
        'flex items-center justify-between rounded-lg border px-4 py-3',
        train.late ? 'border-amber-300 bg-amber-50 text-amber-900' : 'border-slate-200 bg-white text-slate-800',
      );
      item.textContent = train.name + (train.late ? ' (running late)' : '');
      list.append(item);
    }
  </script>
</body>
</html>

Each string is a complete class list, so Tailwind can find every class, and a reviewer sees immediately which classes depend on late.

Common mistakes

  • Thinking the last class wins. It does not; the CSS order decides. Remove the conflicting class instead of adding another.
  • Forgetting tailwindStylesheet in a v4 project. Without it the plugin does not know your custom utilities and theme.
  • Sorting by hand in code review. Let the tool do it; reviewers should discuss design, not order.
  • Adding comments or line breaks inside class attributes. They make diffs noisy and confuse tools.

Key points

  • The order of classes in an attribute never changes the styles; the generated CSS order does.
  • prettier-plugin-tailwindcss sorts classes, removes duplicates and tidies spaces whenever Prettier runs.
  • In Tailwind 4, set tailwindStylesheet to your main CSS file; add tailwindFunctions for clsx, cn or cva.
  • Sorted order: non-Tailwind classes, then utilities, then variants, with breakpoints from small to large.
  • npx @tailwindcss/cli canonicalize "..." rewrites old or arbitrary spellings into standard class names.
  • Long lists repeated in several places belong in a component.

Exercise

Your team in Pune is starting a Vite + React project with Tailwind 4, and the main stylesheet is src/styles/main.css. Write the install command and the .prettierrc so that classes are sorted in className, in clsx() and cn() calls, and in a custom iconClass prop. Then write the npm script that a CI pipeline could run to fail when files are not formatted.

Show solution
Terminal
npm install -D prettier prettier-plugin-tailwindcss
JSON
{
  "plugins": ["prettier-plugin-tailwindcss"],
  "tailwindStylesheet": "./src/styles/main.css",
  "tailwindFunctions": ["clsx", "cn"],
  "tailwindAttributes": ["iconClass"]
}
JSON
{
  "scripts": {
    "format": "prettier --write .",
    "format:check": "prettier --check ."
  }
}

className is sorted by default; tailwindFunctions and tailwindAttributes add the helpers and the custom prop. CI runs npm run format:check, which exits with an error if any file is not formatted.

Quick Quiz

1. An element has class="p-8 p-2". Which padding applies?

2. Which Prettier option tells the plugin where a Tailwind 4 project's CSS entry file is?

3. Where does the plugin put a non-Tailwind class such as card?

4. What did canonicalize turn ml-auto mr-auto into?

Learn Tailwind CSS with a mentor

Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call