A tidy desk with a monitor showing neatly sorted code, a calm gray-blue cat resting in the background.

prettier-plugin-tailwindcss: End the Great Class Order War

The Gray Cat
The Gray Cat
0 views

If you have ever worked on a Tailwind project with more than one person, you know the quiet chaos. One developer writes p-4 flex text-white, another writes flex text-white p-4, and a third sneaks in text-white flex p-4 just to keep things interesting. None of them are wrong, because there is no objectively correct manual order. The result is noisy diffs, pointless review comments, and merge conflicts over whitespace that nobody actually cares about.

prettier-plugin-tailwindcss is the official answer from Tailwind Labs. It is a Prettier plugin that automatically sorts your Tailwind utility classes into one deterministic, recommended order every time you format. There are no new commands to learn and no separate step in your build. You add one line to your Prettier config, and your classes line up the same way for everyone, forever. With roughly 7.7 million weekly downloads, it is the most adopted tool in the entire Tailwind ecosystem, and it is maintained by the same team that builds Tailwind itself.

Why Letting a Machine Sort Wins

The plugin enforces a single canonical order, and the logic behind that order is the clever part. Classes are sorted to mirror the order they appear in the generated stylesheet, which means utilities that override others naturally land later in the list, roughly tracking how the CSS cascade resolves them. Layout and base utilities come first, more specific ones follow, and variant modifiers like hover: and md: are grouped consistently.

Custom classes that are not Tailwind utilities get pushed to the very front, ahead of everything else. That is a deliberate choice: it makes your own bespoke classes easy to spot at a glance instead of being buried somewhere in the middle of a long utility list.

The features worth knowing about:

  • Zero-config sorting for class and className across HTML and JSX/TSX.
  • Framework bindings including Vue :class, Angular [ngClass], Svelte, and Astro.
  • @apply directive sorting inside your CSS files.
  • Function call sorting for helpers like clsx, cva, cn, and tw.
  • Tailwind v4 support built in, alongside continued v3 compatibility.
  • A public sorter API (new in v0.8.0) for sorting classes outside Prettier entirely.

One important boundary: this plugin sorts, it does not lint. It will not warn you about typos, conflicting utilities like flex next to block, or shorthand collisions. For that you reach for something like eslint-plugin-tailwindcss. The two are complementary, not competitors.

Getting It Running

Install Prettier and the plugin as development dependencies.

With npm:

npm install -D prettier prettier-plugin-tailwindcss

With yarn:

yarn add -D prettier prettier-plugin-tailwindcss

A quick note on requirements before you go further. The plugin needs Prettier 3.0 or newer, and version 0.8.0 specifically requires Prettier 3.7.x at minimum. It is also ESM-only, so it cannot be loaded via CommonJS require(). Node 20.19 or newer is expected. None of this matters if you are on a reasonably current setup, but it is worth checking if you are upgrading an older project.

The One-Line Setup

Add the plugin to your Prettier configuration. The simplest case is a single entry in your .prettierrc:

{
  "plugins": ["prettier-plugin-tailwindcss"]
}

That is genuinely the entire setup. Run your normal format command and the classes sort themselves:

npx prettier --write "src/**/*.{ts,tsx,html,css}"

Take a piece of typical JSX before formatting:

function Card() {
  return (
    <div className="text-white p-6 flex shadow-lg rounded-xl bg-slate-800 gap-4 items-center">
      <span className="font-bold text-lg">Hello</span>
    </div>
  );
}

After Prettier runs, the classes are reordered into the recommended sequence:

function Card() {
  return (
    <div className="flex items-center gap-4 rounded-xl bg-slate-800 p-6 text-white shadow-lg">
      <span className="text-lg font-bold">Hello</span>
    </div>
  );
}

Notice that nothing was removed or renamed. The classes are the same; only their order changed. As a bonus, since v0.6.0 the plugin also strips duplicate classes and collapses extra whitespace while it sorts, so accidental repetition gets cleaned up too.

Sorting Classes Hidden Inside JavaScript

Here is the feature that most people miss. By default the plugin only looks at attributes like class and className. But a lot of real Tailwind code lives inside helper functions like clsx, cva, or your own cn utility. Those strings are invisible to the plugin until you tell it where to look.

The tailwindFunctions option registers function names whose string arguments and tagged template literals should be sorted:

// prettier.config.js
export default {
  plugins: ["prettier-plugin-tailwindcss"],
  tailwindFunctions: ["clsx", "cva", "cn", "tw"],
};

With that in place, classes inside those calls get sorted just like attribute values:

import { cva } from "class-variance-authority";

const button = cva("rounded-md px-4 py-2 font-medium text-sm", {
  variants: {
    intent: {
      primary: "text-white bg-blue-600 hover:bg-blue-700",
    },
  },
});

After formatting, both the base string and the variant strings are reordered consistently. If you use a class-merging helper across your codebase and it is not in this list, its classes will silently go unsorted, which is the usual reason people think the plugin is not working.

Reaching Beyond the Default Attributes

Component libraries love custom class props. You might have a containerClassName, an iconClassName, or some other named slot that carries Tailwind utilities. The tailwindAttributes option teaches the plugin to sort those too. It accepts plain attribute names and regex patterns wrapped in slashes:

// prettier.config.js
export default {
  plugins: ["prettier-plugin-tailwindcss"],
  tailwindAttributes: ["containerClassName", "/.*ClassName$/"],
};

The regex form is handy here. The pattern /.*ClassName$/ catches every prop that ends in ClassName in one stroke, so you do not have to enumerate each one by hand.

Pointing at Your Tailwind v4 Setup

As of v0.7.0 the plugin defaults to Tailwind v4. If you use custom utilities or a customized theme defined in your CSS entry point, the plugin needs to read that file to understand your custom class order. The tailwindStylesheet option points it at the right CSS file. The path resolves relative to your Prettier config:

// prettier.config.js
export default {
  plugins: ["prettier-plugin-tailwindcss"],
  tailwindStylesheet: "./src/styles/app.css",
};

If you are still on Tailwind v3, the equivalent is tailwindConfig, which points at your tailwind.config.js. By default the plugin searches from the config directory, but you will want to set it explicitly in monorepos or non-standard layouts. From v0.7.0 onward, configs are loaded relative to the input file, which makes monorepo setups behave far more sensibly than they used to.

Stacking Plugins Without Breaking Things

This is the gotcha that bites teams who use more than one Prettier plugin. prettier-plugin-tailwindcss must be listed last in your plugins array. It contains internal logic that sequentially loads other known plugins, but the ordering in your config still matters. Put it anywhere but last and sorting or compatibility can quietly break.

So if you also sort imports, the Tailwind plugin goes at the bottom:

{
  "plugins": [
    "@ianvs/prettier-plugin-sort-imports",
    "prettier-plugin-tailwindcss"
  ]
}

The plugin declares optional peer dependencies for around seventeen other Prettier plugins it is known to play nicely with, including the Svelte, Astro, and Marko plugins, the popular import sorters, and Liquid, Twig, and Pug formatters. As long as Tailwind comes last, stacking them generally just works.

Sorting Outside of Prettier

Version 0.8.0 introduced a feature that opens up a lot of possibilities: a public sorter API exposed through a dedicated /sorter entry point. This lets you run the exact same sorting logic outside of Prettier entirely, which is useful for codemods, custom tooling, or your own lint rule.

import { createSorter } from "prettier-plugin-tailwindcss/sorter";

const sorter = await createSorter();

const sorted = sorter.sortClassList("text-white p-4 flex bg-blue-600");
// "flex bg-blue-600 p-4 text-white"

You get methods for sorting HTML class attribute strings and arrays of classes. The exact shape may evolve, but the headline is that the canonical order is no longer locked inside the formatter. If you are building a migration script or generating markup programmatically, you can produce correctly sorted classes without spinning up Prettier.

The same release also trimmed startup overhead: it removed a top-level await, dropped the recast and ast-types dependencies, added directory-based config-resolution caching, and now lazy-loads the Tailwind v3 versus v4 modules only when they are actually needed. It has zero runtime dependencies by design, which keeps it slim.

Worth Knowing Before You Commit

A few honest limitations keep expectations grounded. The plugin sorts but never validates, so pair it with an ESLint Tailwind plugin if you want protection against contradictory utilities. Sorting inside JavaScript helpers only happens for functions you explicitly register in tailwindFunctions, and class strings assembled dynamically at runtime cannot be sorted because they do not exist as literals in your source. On Tailwind v4, remember to point tailwindStylesheet at your CSS entry so custom utilities sort the way you expect.

Closing the War

The pitch for prettier-plugin-tailwindcss is almost embarrassingly simple. One line in your Prettier config buys you consistent class order across an entire team, cleaner diffs, fewer pointless review comments, and one less thing to think about on every save. It is official, it tracks new Tailwind versions faster than anyone else, and it folds into the formatter your team already runs.

Add it, list it last, register your class-merging helpers in tailwindFunctions, and then never argue about whether flex comes before p-4 again. The machine has opinions now, and they are the same opinions for everybody.