An ESLint plugin for React & TypeScript teams

Stop leaving the same comment on every pull request.

eslint-frontend-rules turns the review nitpicks — raw <p> tags, hardcoded hex colors, unstable default props, missing alt text — into lint errors your team catches before a human has to type them again. Twenty-eight rules, six with autofix, one plugin.

$ npm install --save-dev eslint-frontend-rules
View on npm → View source on GitHub →
28lint rules
9categories covered
6ship with autofix
npm downloads / mo
GitHub stars

See it work

One component, six review comments you'll never write again.

This is what a single lint pass over one component actually flags — a deep import, a default export, a color literal, an unlabeled click handler, and a raw tag. Toggle to see the fix.

card.tsx
import Button from '../../../components/button'1;

export default2 function Card({ items = []3 }) {
  return (
    <div style={{ color: '#4285F4'4 }} onClick={onSelect}5>
      <p>6Total: {items.length}</p>
    </div>
  );
}
  • 1enforce-alias-import-pathsdeep relative import; use an alias like @/components/button.
  • 2no-default-exportdefault export found; use a named export instead.
  • 3no-unstable-default-propsnew array literal on every render; breaks memoization downstream.
  • 4no-direct-colorsdirect color value; use a CSS variable or theme token.
  • 5no-focusable-non-interactive-elementsclick handler on a non-interactive element with no role or onKeyDown.
  • 6enforce-typography-componentsraw <p> tag; use a Typography component like TypographyP.

Why teams add it

The consistency your style guide already asks for, enforced automatically.

Design consistency

Raw <p>/<span>/<h1><h6> tags and hardcoded hex/rgb/hsl colors get flagged in both JSX and SVG attributes, so Typography components and design tokens stay the only way in.

design · 3 rules

Accessibility by default

Missing alt text, icon-only buttons with no accessible name, and click handlers on non-interactive elements are caught at lint time — not in a screen-reader audit three sprints later.

accessibility · 3 rules

React correctness

Components nested inside components, unstable default props, unnecessary fragments and curlies — the patterns that quietly break memoization and re-render more than they should.

react · 9 rules

Naming, settled once

Boolean props, enum members, hook names, filenames, and CSS Module import aliases all follow one convention — decided in config, not re-litigated in every review thread.

naming & typescript · 6 rules

Quick start

Install it, extend the recommended config, done.

Requires ESLint 9 or newer (flat config). Install as a dev dependency, add it to eslint.config.js, and every rule in the recommended set turns on immediately — override any individual rule's level or options underneath.

$ npm install --save-dev eslint-frontend-rules
eslint.config.js
import frontendRules from "eslint-frontend-rules";

export default {
  extends: [frontendRules.configs.recommended],
  plugins: {
    "eslint-frontend-rules": frontendRules,
  },
  rules: {
    // Optionally override rule levels or options here
  },
};

Autofix in action

Six rules rewrite the code for you.

Most naming and typing rules can't be autofixed safely — renaming a symbol without an AST-aware rename would break every call site. These can.

enforce-use-state-naming Autofix
const [name, updateName] = useState(""); const [name, setName] = useState("");

Every setter reads as [value, setValue] — the fix renames the binding and every reference to it in scope.

enforce-css-module-import-name Autofix
import css from './card.module.css'; import styles from './card.module.css';

One local name for CSS Module imports across the whole codebase — grep for styles. and mean it.

interface-type-required-first Autofix
interface Props { label?: string; id: string; } interface Props { id: string; label?: string; }

Required fields first, always — an interface reads like a function signature, not a grab bag.

The rulebook

All 28 rules, in one place.

Every rule accepts an ignore option (glob patterns) to exempt files; several take their own additional options — see the README for the full list.

  • enforce-typography-componentsEnforces Typography components over raw <p>, <span>, <h1>–<h6>, <blockquote> tags.Design & Color
  • no-direct-colorsDisallows hex, rgb(a), hsl(a), and named colors in style props and classNames.Design & Color
  • no-direct-colors-in-svg-attrsExtends the color rule to SVG fill, stroke, stopColor, and flood/lighting-color attributes.Design & Color
  • enforce-icon-button-aria-labelRequires an accessible name (aria-label, aria-labelledby, or title) on icon-only buttons.Accessibility
  • no-focusable-non-interactive-elementsFlags non-interactive elements with onClick but no role or onKeyDown.Accessibility
  • no-img-missing-altRequires an alt attribute on raw <img> elements — empty alt is fine, missing isn't.Accessibility
  • enforce-classname-utilityWarns when className is built from a template string instead of a class-merging utility like cn.React Patterns
  • enforce-event-handler-namingRequires on* prop names and handle* local function names for event handlers.React PatternsAutofix
  • enforce-no-empty-classname-utilityDisallows empty or whitespace-only className attributes.React Patterns
  • enforce-use-state-namingRequires the [value, setValue] naming convention for useState destructuring.React PatternsAutofix
  • no-inline-arrow-functions-in-jsxWarns on inline arrow functions passed as JSX props.React Patterns
  • no-nested-componentDisallows defining a component inside another component's body.React Patterns
  • no-unnecessary-curly-in-propsWarns on curly braces wrapping a string literal in a JSX prop.React PatternsAutofix
  • no-unnecessary-fragmentWarns when a React fragment wraps a single child or isn't needed for grouping.React Patterns
  • no-unstable-default-propsDisallows object/array/function literal defaults in component or hook parameters.React Patterns
  • enforce-boolean-prop-namingRequires boolean props to start with is, has, should, can, will, or did.Naming
  • enforce-css-module-import-nameEnforces one consistent local name (default styles) for CSS Module imports.NamingAutofix
  • enforce-interface-type-namingInterfaces start with I or end with Props; types start with T or end with Props.Naming
  • top-level-const-snakeRequires top-level const names in .tsx files to be ALL_CAPS.Naming
  • enforce-enum-member-namingEnforces PascalCase or UPPER_SNAKE_CASE for enum members.TypeScript
  • interface-type-required-firstRequires required fields before optional fields in interfaces and type literals.TypeScriptAutofix
  • enforce-alias-import-pathsFlags deep ../../ relative imports in favor of configured alias prefixes.Imports & Exports
  • no-default-exportDisallows default exports; enforces named exports only.Imports & Exports
  • enforce-kebab-case-filenamesEnforces kebab-case file names for configured extensions.File Structure
  • require-jsdoc-on-componentWarns if a root-level React component lacks a JSDoc comment.Documentation
  • require-jsdoc-on-hookWarns if a root-level custom hook lacks a JSDoc comment.Documentation
  • require-jsdoc-on-root-functionWarns if a root-level plain function lacks a JSDoc comment.Documentation
  • enforce-testid-namingEnforces kebab-case values for data-testid (or a configured attribute).TestingAutofix