# eslint-plugin-tailwindcss

> Rules enforcing best practices while using Tailwind CSS

Latest version **4.4.0** (published 2026-08-21) · MIT license · 0 weekly downloads

## Install

```sh
npm install eslint-plugin-tailwindcss
pnpm add eslint-plugin-tailwindcss
yarn add eslint-plugin-tailwindcss
bun add eslint-plugin-tailwindcss
```

## Health

**Score 70/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; recently updated; high maintenance score; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 4.4.0 |
| Published | 2026-08-21 |
| First published | 2021-03-25 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Node | >=20.19.0 |
| Dependencies | 5 |
| Unpacked size | 123.9 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 2075 |
| Author | François Massart |
| Maintainers | francoismassart |
| Keywords | eslint, eslintplugin, eslint-plugin, tailwind, tailwindcss, typescript |

## Links

- npm: https://www.npmjs.com/package/eslint-plugin-tailwindcss
- Repository: https://github.com/francoismassart/eslint-plugin-tailwindcss
- Issues: https://github.com/francoismassart/eslint-plugin-tailwindcss/issues
- npm.io page: https://npm.io/package/eslint-plugin-tailwindcss

## Dependencies (5)

- [postcss](https://npm.io/package/postcss.md) ^8.4.4
- [synckit](https://npm.io/package/synckit.md) ^0.11.11
- [postcss-nested](https://npm.io/package/postcss-nested.md) ^7.0.2
- [tailwind-api-utils](https://npm.io/package/tailwind-api-utils.md) ^1.0.3
- [@typescript-eslint/utils](https://npm.io/package/@typescript-eslint/utils.md) ^8.37.0

## Alternatives

- [eslint-plugin-sonarjs](https://npm.io/package/eslint-plugin-sonarjs.md) — 2.9M weekly downloads
- [eslint-config-expo](https://npm.io/package/eslint-config-expo.md) — 1.5M weekly downloads
- [@matter/protocol](https://npm.io/package/@matter/protocol.md) — 63.5K weekly downloads
- [@eventcatalog/linter](https://npm.io/package/@eventcatalog/linter.md) — 24.8K weekly downloads
- [@pandacss/eslint-plugin](https://npm.io/package/@pandacss/eslint-plugin.md) — 18.7K weekly downloads

## Recent versions

- 4.4.0 (latest) — 2026-08-21
- 4.4.1-beta.1 (beta) — 2026-08-24
- 4.0.0-alpha.1 (next) — 2026-04-13
- 2.0.1 (tw2) — 2022-01-21
- 2.0.1-beta.1 (tw2beta) — 2022-01-21
- 4.4.1-beta.0 — 2026-08-24
- 4.4.0-beta.0 — 2026-08-19
- 4.3.0 — 2026-08-17
- 4.3.0-svelte.beta.2 — 2026-08-13
- 4.3.0-svelte.beta.1 — 2026-08-12
- 4.3.0-svelte.beta.0 — 2026-07-17
- 4.2.0 — 2026-07-13
- 4.2.0-beta.1 — 2026-07-13
- 4.2.0-beta.0 — 2026-07-13
- 4.1.0 — 2026-07-10
- … 200 more at https://npm.io/package/eslint-plugin-tailwindcss/versions

## README

<div align="center">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="images/logo-dark.png">
    <source media="(prefers-color-scheme: light)" srcset="images/logo-light.png">
    <img alt="eslint-plugin-tailwindcss" src="images/logo-dark.png">
  </picture>
</div>
<div style="display:flex;flex-direction:row;gap:10px;justify-content:center;"><img alt="npm latest version" src="https://img.shields.io/npm/v/eslint-plugin-tailwindcss?style=for-the-badge" /> <img alt="license" src="https://img.shields.io/npm/l/eslint-plugin-tailwindcss?style=for-the-badge" /> <img alt="downloads" src="https://img.shields.io/npm/dt/eslint-plugin-tailwindcss?style=for-the-badge" /></div>

# `eslint-plugin-tailwindcss`

- Best practices & consistency since 2021
- [Made for Tailwind CSS v4](./CHANGELOG.md#made-for-tailwind-css-v4)
- [9 rules available](#rules) and more on the way
- What's new? [Changelog](./CHANGELOG.md) | [Release notes](https://github.com/francoismassart/eslint-plugin-tailwindcss/releases) | [Roadmap](./ROADMAP.md)
- [Upgrade guide](./UPGRADE.md) from `v3` to `v4`

## This project needs your help

<p align="center">
  <a href="https://github.com/sponsors/francoismassart">
      <img alt="Support eslint-plugin-tailwindcss" src="images/support.png">
    </picture>
  </a>
</p>
<p align="center">I spent countless days working on this plugin, and it is available to everyone for free.<br>If you benefit from my work and if you want to help me keep the project alive, consider becoming a sponsor.</p>

| Premium sponsors                                                                                                                                     | Current sponsors                                                                                                                                                                                                                                                                                                                                                                                                                            |
| :--------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| <a href="https://www.sent.dm/" target="_blank"><img alt="Sent.dm" width="100" src="https://avatars.githubusercontent.com/u/153308555?s=200&v=4"></a> | <a href="https://github.com/codecov" target="_blank"><img class="avatar" src="https://avatars.githubusercontent.com/u/8226205?s=100&amp;v=4" width="50" height="50" style="border-radius:100%;" alt="@codecov"></a> <a href="https://github.com/getsentry" target="_blank"><img class="avatar" src="https://avatars.githubusercontent.com/u/1396951?s=100&amp;v=4" width="50" height="50" style="border-radius:100%;" alt="@getsentry"></a> |

<p align="center"><a href="https://github.com/sponsors/francoismassart">GitHub Sponsors</a> | <a href="https://thanks.dev/r/gh/francoismassart">thanks.dev Sponsors</a></p>

## Rules

<!-- begin auto-generated rules list -->

💼 Configurations enabled in.\
⚠️ Configurations set to warn in.\
✅ Set in the `recommended` configuration.\
🔧 Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).\
💡 Manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions).

| Name                                                                                   | Description                                                                               | 💼  | ⚠️  | 🔧  | 💡  |
| :------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------- | :-- | :-- | :-- | :-- |
| [classnames-order](docs/rules/classnames-order.md)                                     | Enforces a consistent order for the Tailwind CSS classnames, based on the compiler.       |     | ✅  | 🔧  |     |
| [enforces-canonical-classname](docs/rules/enforces-canonical-classname.md)             | Enforces the canonical spelling of a classname, as resolved by the Tailwind CSS compiler. |     | ✅  | 🔧  |     |
| [enforces-negative-arbitrary-values](docs/rules/enforces-negative-arbitrary-values.md) | Warns about `-` prefixed classnames using arbitrary values.                               |     | ✅  | 🔧  |     |
| [enforces-shorthand](docs/rules/enforces-shorthand.md)                                 | Avoid using multiple Tailwind CSS classnames when not required.                           |     | ✅  | 🔧  |     |
| [important-modifier-suffix](docs/rules/important-modifier-suffix.md)                   | In v4 you should place the `!` at the very end of the class name.                         |     | ✅  | 🔧  |     |
| [no-arbitrary-value](docs/rules/no-arbitrary-value.md)                                 | Forbid using arbitrary values in classnames.                                              |     |     |     |     |
| [no-contradicting-classname](docs/rules/no-contradicting-classname.md)                 | Avoid contradicting Tailwind CSS classnames.                                              | ✅  |     |     | 💡  |
| [no-custom-classname](docs/rules/no-custom-classname.md)                               | Detects classnames which do not belong to Tailwind CSS.                                   |     | ✅  |     | 💡  |
| [no-unnecessary-arbitrary-value](docs/rules/no-unnecessary-arbitrary-value.md)         | Avoid unjustified arbitrary classnames.                                                   |     | ✅  | 🔧  | 💡  |

<!-- end auto-generated rules list -->

## Getting started

### 1. Install the plugin

`npm i -D eslint-plugin-tailwindcss`

- As it is a dev dependency, it will not affect your final bundle size 🪶
- It can be installed with other package managers such as `pnpm`
- We support ESLint v10

> If you are still using Tailwind CSS v3 and/or older ESLint versions, you can use `eslint-plugin-tailwindcss@3.x.x`

### 2. Edit your `eslint.config` file

Here is a very basic example:

```js
// 1. Import the plugin and defineConfig helper
import eslintPluginTailwindcss from "eslint-plugin-tailwindcss";
import { defineConfig } from "eslint/config";

export default defineConfig([
  // 2. Include the recommended config
  eslintPluginTailwindcss.configs["flat/recommended"] ||
    eslintPluginTailwindcss.configs.recommended,

  // 3. Define your own settings
  {
    settings: {
      // Define the tailwindcss settings with the MANDATORY `cssConfigPath`
      tailwindcss: {
        cssConfigPath: "./src/styles/tailwind.css",
      },
    },
    // Optional: Customize the rules to your needs
    rules: {
      "tailwindcss/classnames-order": "warn",
      "tailwindcss/no-arbitrary-value": "warn",
      "tailwindcss/no-custom-classname": [
        "warn",
        { whitelist: ["custom\\-*"] },
      ],
      "tailwindcss/no-contradicting-classname": "warn",
    },
  },
]);
```

> `cssConfigPath` can be an absolute or a relative path. If you provide a relative path, the plugin will attempt to convert it into an absolute path.

#### Typesafe settings 🤓

The plugin also exports the type `PluginSettings` which you can use to benefit from autocomplete and validation directly inside your `eslint.config` file.

1. You may need to add `// @ts-check` to force type checking
2. Add the magic JSDoc comment `/** @type {import('eslint-plugin-tailwindcss').PluginSettings} */`

Here is a snippet, notice the object is surrounded by parentheses `({...})`:

```js
settings: {
  tailwindcss:
    /** @type {import('eslint-plugin-tailwindcss').PluginSettings} */
    ({
      cssConfigPath: './styles/tailwind.css',
    }),
},
```

### 3. Going further

You can also:

- Run the linting process inside a pipeline of your hosted repository. I recommend running it on your merge/pull requests, rather than on every single (pre-)commit.
- Auto-format on save in your favorite IDE.

## Settings

Most of the rules solely use the shared settings (shared across all the plugin rules). [Learn about eslint shared settings](https://eslint.org/docs/latest/use/configure/configuration-files#configuring-shared-settings) from the official documentation.

Here is a fully detailed example of shared settings:

```js
// eslint.config.mjs
{
  settings: {
    tailwindcss: {
      // Attributes/props that could contain Tailwind CSS classes...
      // Optional, default values: ["class", "className", "ngClass", "@apply"]
      attributes: ["class"],
      // The (absolute or relative) path pointing to you main Tailwind CSS v4 config file.
      // It must be a `.css` file (v4), not a `.js` file (v3)
      // REQUIRED, as the default value may not work out-of-the-box
      cssConfigPath: "./styles/tailwind.css",
      // Functions/tagFunctions that will be parsed by the plugin.
      // Optional, default values: ["classnames", "classNames", "clsx", "cn", "ctl", "cva", "tv", "tw", "twMerge", "twJoin"]
      functions: ["twClasses"],
      // Within the list of functions, which should we check the keys instead of the values (used for `clsx`, etc.)
      // Optional, default values: ["classnames", "classNames", "clsx"]
      parseKeyFunctions: ["clsx"],
      // Keys to be ignored in object expressions
      // Optional, default values: ["defaultVariants", "compoundVariants", "compoundSlots"]
      ignoredKeys: ["defaultVariants", "compoundVariants", "compoundSlots", "specificKey"],
      // Max size of the Set or Map objects used for caching
      // Optional, default value: 250_000
      cacheMaxSize: 150_000,
      // Max lifetime of the cache set in ms
      // Optional, default value: 10 * 60 * 1000 (10 minutes)
      cacheMaxAge: 60 * 1000,
    },
  }
}
```

The default settings are exported via the [`DEFAULT_SETTINGS`](src/utils/parse-plugin-settings.ts).

## Contributing

The project is open to all developers, you can [contribute to `eslint-plugin-tailwindcss`](CONTRIBUTING.md).

However, make sure to discuss the issue or the feature you wish to implement prior to getting to work unless you feel adventurous.

---
_Source: https://npm.io/package/eslint-plugin-tailwindcss · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
