React SME Cookbook
All FAQs

Search Documentation

Search across all documentation pages

eslintpluginsreacttypescriptaccessibilitytailwindtesting

ESLint Plugins

Install and configure ESLint plugins for React, TypeScript, accessibility, Tailwind CSS, and testing.

Recipe

Quick-reference recipe card — copy-paste ready.

# Core plugins (most included with next/core-web-vitals)
npm install --save-dev eslint-plugin-react eslint-plugin-react-hooks
 
# TypeScript
npm install --save-dev @typescript-eslint/eslint-plugin @typescript-eslint/parser
 
# Imports
npm install --save-dev eslint-plugin-import
 
# Accessibility
npm install --save-dev eslint-plugin-jsx-a11y
 
# Tailwind CSS
npm install --save-dev eslint-plugin-tailwindcss
 
# Testing Library
npm install --save-dev eslint-plugin-testing-library

When to reach for this: When you need rules beyond what next/core-web-vitals provides — Tailwind class ordering, test best practices, or stricter accessibility checks.

Working Example

// eslint.config.mjs
import { FlatCompat } from "@eslint/eslintrc";
import tailwindcss from "eslint-plugin-tailwindcss";
import { dirname } from "path";
import { fileURLToPath } from "url";
 
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
 
const compat = new FlatCompat({ baseDirectory: __dirname });
 
const eslintConfig = [
  // Next.js presets (includes react, react-hooks, import, jsx-a11y)
  ...compat.extends("next/core-web-vitals", "next/typescript"),
 
  // Tailwind CSS plugin (flat config native)
  ...tailwindcss.configs["flat/recommended"],
 
  // Testing Library (only for test files)
  {
    files: ["**/*.test.{ts,tsx}", "**/*.spec.{ts,tsx}"],
    ...compat.extends("plugin:testing-library/react")[0],
  },
 
  // Custom rule overrides
  {
    rules: {
      // Tailwind
      "tailwindcss/classnames-order": "warn",
      "tailwindcss/no-custom-classname": "off",
 
      // Accessibility
      "jsx-a11y/anchor-is-valid": "warn",
      "jsx-a11y/alt-text": "error",
 
      // TypeScript
      "@typescript-eslint/no-unused-vars": [
        "error",
        { argsIgnorePattern: "^_" },
      ],
      "@typescript-eslint/consistent-type-imports": "error",
 
      // Imports
      "import/order": [
        "warn",
        {
          groups: [
            "builtin",
            "external",
            "internal",
            ["parent", "sibling"],
            "index",
            "type",
          ],
          "newlines-between": "always",
          alphabetize: { order: "asc" },
        },
      ],
    },
  },
];
 
export default eslintConfig;

What this demonstrates:

  • Layering plugins on top of Next.js presets
  • Tailwind plugin using its native flat config support
  • Scoping the testing-library plugin to only test files
  • Import ordering with group separation

Deep Dive

How It Works

  • Plugins provide collections of rules under a namespace (e.g., react/, jsx-a11y/)
  • next/core-web-vitals already includes eslint-plugin-react, eslint-plugin-react-hooks, eslint-plugin-next, eslint-plugin-import, and eslint-plugin-jsx-a11y
  • Additional plugins like tailwindcss and testing-library are added on top
  • In flat config, some plugins export configs["flat/recommended"] directly; others need FlatCompat

Variations

Plugin overview:

PluginNamespaceWhat It Does
eslint-plugin-reactreact/JSX best practices, component patterns
eslint-plugin-react-hooksreact-hooks/Hook rules and dependency checking
@typescript-eslint/eslint-plugin@typescript-eslint/TypeScript-specific rules
eslint-plugin-importimport/Import ordering, no duplicates, no unresolved
eslint-plugin-jsx-a11yjsx-a11y/Accessibility rules for JSX elements
eslint-plugin-tailwindcsstailwindcss/Class ordering, no contradicting classes
eslint-plugin-testing-librarytesting-library/Best practices for Testing Library

Tailwind plugin key rules:

{
  rules: {
    "tailwindcss/classnames-order": "warn",        // Sort classes
    "tailwindcss/no-custom-classname": "off",       // Allow custom classes
    "tailwindcss/no-contradicting-classname": "error", // Catch p-4 p-8
    "tailwindcss/enforces-negative-arbitrary-values": "warn",
  },
}

Testing Library plugin (scoped to test files):

{
  files: ["**/*.test.{ts,tsx}", "**/*.spec.{ts,tsx}"],
  rules: {
    "testing-library/await-async-queries": "error",
    "testing-library/no-debugging-utils": "warn",
    "testing-library/no-dom-import": "error",
    "testing-library/prefer-screen-queries": "warn",
  },
}

TypeScript Notes

// @typescript-eslint key rules:
// no-unused-vars — catches unused imports and variables
// no-explicit-any — flags `any` type usage
// consistent-type-imports — enforces `import type { X }` syntax
// no-non-null-assertion — flags `obj!.prop` assertions
// prefer-nullish-coalescing — flags `||` where `??` is safer
 
// These complement tsconfig strict mode but catch
// different things (patterns vs types)

Gotchas

Things that will bite you. Each gotcha includes what goes wrong, why it happens, and the fix.

  • Duplicate plugin registration — Installing eslint-plugin-react manually when next/core-web-vitals already includes it causes conflicts. Fix: Check what the preset includes before adding plugins. Only add plugins not already bundled.

  • Tailwind plugin requires configeslint-plugin-tailwindcss needs to find your tailwind.config.* file. If your config is in a non-standard location, the plugin fails silently. Fix: Set the config option in the plugin settings.

  • Testing plugin on all files — Applying testing-library rules globally flags non-test code. Fix: Always scope with files: ["**/*.test.*"].

  • Flat config compatibility — Not all plugins support flat config natively. Some need FlatCompat wrapping, which can cause unexpected behavior. Fix: Check the plugin's README for flat config support and use FlatCompat only when needed.

  • Performance with many plugins — Each plugin adds parsing and rule-checking time. Five or more plugins can noticeably slow down linting. Fix: Use TIMING=1 npx eslint . to profile which rules are slow and disable any you do not need.

Alternatives

Other ways to solve the same problem — and when each is the better choice.

AlternativeUse WhenDon't Use When
BiomeYou want linting + formatting in one fast toolYou need Tailwind or testing-library rules
next/core-web-vitals onlyThe bundled plugins are sufficient for your projectYou need Tailwind class ordering or test rules
oxlintYou want blazing-fast linting with common rulesYou need plugin-specific rules (Tailwind, testing)

FAQs

Which plugins are already included in next/core-web-vitals?
  • eslint-plugin-react
  • eslint-plugin-react-hooks
  • eslint-plugin-next
  • eslint-plugin-import
  • eslint-plugin-jsx-a11y

You do not need to install these separately.

Gotcha: What happens if I install a plugin that next/core-web-vitals already includes?
  • You get duplicate plugin registration, which causes conflicts and unexpected rule behavior.
  • Check what the preset includes before manually adding plugins.
  • Only add plugins not already bundled by the preset.
How do I scope a plugin to specific file types?
{
  files: ["**/*.test.{ts,tsx}", "**/*.spec.{ts,tsx}"],
  rules: {
    "testing-library/await-async-queries": "error",
    "testing-library/prefer-screen-queries": "warn",
  },
}

Use the files property to restrict rules to matching globs.

What does eslint-plugin-tailwindcss provide?
  • tailwindcss/classnames-order sorts Tailwind classes in the recommended order.
  • tailwindcss/no-contradicting-classname catches conflicts like p-4 p-8.
  • tailwindcss/no-custom-classname flags classes not in the Tailwind config (often turned off).
How does the Tailwind plugin find my tailwind.config file?
  • It looks for tailwind.config.* in the project root by default.
  • If your config is in a non-standard location, set the config option in the plugin settings.
  • Without the correct config path, the plugin fails silently.
What is the difference between flat config native plugins and FlatCompat plugins?
  • Flat config native plugins export configs["flat/recommended"] directly.
  • Older plugins need FlatCompat wrapping to work with ESLint 9 flat config.
  • Check each plugin's README for flat config support status.
Gotcha: Why does adding many plugins slow down linting?
  • Each plugin adds its own parsing and rule-checking overhead.
  • Five or more active plugins can noticeably slow down linting.
  • Profile with TIMING=1 npx eslint . to find the slowest rules and disable those you do not need.
How do I add TypeScript-specific rules via @typescript-eslint?
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
"@typescript-eslint/consistent-type-imports": "error",
"@typescript-eslint/no-explicit-any": "warn",

These are included via next/typescript but you can override their severity.

Can I use eslint-plugin-testing-library on all files?
  • No. Applying it globally flags non-test code with false positives.
  • Always scope it with files: ["**/*.test.*", "**/*.spec.*"].
How do I check which plugin provides a specific rule?
  • Rules are prefixed with the plugin namespace: react/, @typescript-eslint/, jsx-a11y/, etc.
  • Run npx eslint --print-config src/app/page.tsx to see all active rules and their sources.
What accessibility rules does jsx-a11y provide?
  • jsx-a11y/alt-text requires alt attributes on images.
  • jsx-a11y/anchor-is-valid warns about anchors without valid href.
  • The plugin checks ARIA roles, keyboard accessibility, and form labels.
Should I use TypeScript ESLint rules or tsconfig strict options?
  • Use both. They catch different things.
  • @typescript-eslint catches code patterns (unused vars, floating promises, explicit any).
  • tsconfig strict options catch type-level issues (null checks, implicit any, unchecked index access).