Skip to content

Migrating from eslint-plugin-better-tailwindcss ​

eslint-plugin-better-tailwindcss runs in ESLint and, as a JS plugin, in oxlint. oxlint-tailwindcss is built for oxlint and Tailwind v4. Each of better-tailwindcss 4.7.0's 15 rules has a counterpart here, and each pair reports the same example — the tables below are checked against it every week, with its latest release.

Rules ​

better-tailwindcssoxlint-tailwindcssNotes
enforce-consistent-class-orderenforce-sort-orderTheir default order: "official" is this rule's order. Unknown classes go first, in the order written, as with their unknownClassOrder: "preserve". order: "strict" → mode: "strict", which also groups classes by variant chain (the order within can differ). asc, desc and the componentClass* / unknownClass* options have no equivalent.
enforce-consistent-important-positionenforce-consistent-important-positionposition: "recommended" → "suffix" (the default, p-4!); "legacy" → "prefix" (!p-4).
enforce-consistent-line-wrappingenforce-consistent-line-wrappingprintWidth, classesPerLine and group mean the same. The autofix only rewrites template literals — a string attribute is reported, not rewritten — and a line over printWidth is rewrapped only with wrapLines. indent, tabWidth, lineBreakStyle, preferSingleLine, strictness and vueConvertToBinding have no equivalent: the indentation comes from the source.
enforce-consistent-variant-orderconsistent-variant-orderBoth write chains outermost first (sm:hover:). This rule also orders color scheme and attribute variants (dark:hover:, data-[state=open]:hover:), which theirs leaves alone; order sets your own.
enforce-consistent-variable-syntaxenforce-consistent-variable-syntaxsyntax: "shorthand" is the default in both; "variable" → "explicit".
enforce-shorthand-classesenforce-shorthand
enforce-logical-propertiesenforce-logicalignore → allowlist (regular expressions, too). direction limits it to inline or block utilities; enforce-physical is the reverse rule.
enforce-canonical-classesenforce-canonicalTheir collapse (merging mt-2 mb-2 into my-2) is enforce-shorthand here. ignore and logical have no equivalent.
no-duplicate-classesno-duplicate-classes
no-deprecated-classesno-deprecated-classes
no-unnecessary-whitespaceno-unnecessary-whitespaceLine breaks are always kept, as with their allowMultiline: true.
no-concatenated-classesno-dynamic-classesBoth report a class built with ${} or +. This one reports it when the built class starts with a Tailwind utility or variant, so "p-4 " + extra and `icon-${name}` are fine.
no-unknown-classesno-unknown-classesignore (regular expressions) → allowlist (exact names) and ignorePrefixes. The classes your CSS defines (@layer components, @utility) are always known, so their detectComponentClasses setting has no counterpart.
no-conflicting-classesno-conflicting-classesIt also reports a class another one makes redundant (reportRedundant), and allow exempts pairs.
no-restricted-classesno-restricted-classesrestrict: [{ pattern, message }] → patterns: [{ pattern, message }], and classes for exact names. There is no fix.

Settings ​

settings["better-tailwindcss"]settings.tailwindcssNotes
entryPointentryPointRequired here. A string, or a glob → CSS mapping for a monorepo.
cwdentryPointMap each package's files to its CSS with entryPoint: [{ files, use }], or give the package its own .oxlintrc.json.
rootFontSizerootFontSize
selectorsattributes, attributePatterns, callees, calleeExtractors, tags, variablePatternsBoth read class / className, the same variable names and most helpers by default. Add what yours lists that this one doesn't: the dcnb helper to callees, the twc / twx tags to tags.
detectComponentClasses—Always on: the classes your CSS defines are known.
tailwindConfig—A Tailwind v3 JavaScript config; this plugin reads Tailwind v4 CSS.
tsconfig—
messageStyle—

Switching, step by step ​

  1. Install the plugin: pnpm add -D oxlint-tailwindcss (and oxlint, if better-tailwindcss ran in ESLint).
  2. In .oxlintrc.json, add "jsPlugins": ["oxlint-tailwindcss"], and move settings["better-tailwindcss"] to settings.tailwindcss with the settings table. entryPoint is required.
  3. Rename each better-tailwindcss/<rule> to the tailwindcss/<rule> of the rules table, turning its options into the ones its notes name. Or start from the recommended config and add what you had on top.
  4. Remove eslint-plugin-better-tailwindcss — from jsPlugins, or from the ESLint config — and uninstall it, so the two don't report the same class.
  5. Run oxlint, then oxlint --fix for the autofixes.

A typical config, before and after:

jsonc
// Before
{
  "jsPlugins": ["eslint-plugin-better-tailwindcss"],
  "settings": { "better-tailwindcss": { "entryPoint": "src/styles.css" } },
  "rules": {
    "better-tailwindcss/enforce-consistent-class-order": "warn",
    "better-tailwindcss/enforce-consistent-important-position": ["warn", { "position": "legacy" }],
    "better-tailwindcss/no-unknown-classes": ["error", { "ignore": ["^swiper-"] }],
    "better-tailwindcss/no-concatenated-classes": "error"
  }
}

// After
{
  "jsPlugins": ["oxlint-tailwindcss"],
  "settings": { "tailwindcss": { "entryPoint": "src/styles.css" } },
  "rules": {
    "tailwindcss/enforce-sort-order": "warn",
    "tailwindcss/enforce-consistent-important-position": ["warn", { "position": "prefix" }],
    "tailwindcss/no-unknown-classes": ["error", { "ignorePrefixes": ["swiper-"] }],
    "tailwindcss/no-dynamic-classes": "error"
  }
}

What else changes ​

  • Vue, Svelte and Astro templates. In oxlint, JS plugins see only the <script> blocks of those files — better-tailwindcss run in ESLint with a framework parser reads templates too. See Vue, Svelte & Astro.
  • Tailwind v3. This plugin reads Tailwind v4 CSS (v4.1.15 and later); a tailwind.config.js project needs to stay on better-tailwindcss.

Rules better-tailwindcss doesn't have ​

Worth a look once you've switched:

enforce-negative-arbitrary-values · enforce-physical · max-class-count · no-arbitrary-value · no-borrowed-component-styles · no-contradicting-variants · no-dark-without-light · no-default-palette · no-hardcoded-colors · no-unnecessary-arbitrary-value · prefer-scale-token · prefer-theme-tokens

Released under the MIT License.