logoESLint React

@eslint-react/kit

ESLint React's toolkit for building custom React rules with JavaScript functions

This module is currently in beta. APIs may change in future releases.

Installation

npm install --save-dev @eslint-react/kit

Quick Start

eslint.config.ts
import  from "@eslint-react/eslint-plugin";
import , {  } from "@eslint-react/kit";
import type {  } from "@eslint-react/kit";
import  from "@eslint/js";
import {  } from "eslint/config";
import  from "typescript-eslint";

/** Enforce function declarations for function components. */
function ():  {
  return (, {  }) => {
    const { ,  } = .();
    return (
      ,
      {
        "Program:exit"() {
          for (const {  } of .()) {
            if (. === "FunctionDeclaration") continue;
            .({
              ,
              : "Function components must be defined with function declarations.",
            });
          }
        },
      },
    );
  };
}

export default (
  {
    : ["**/*.{ts,tsx}"],
    : [
      ..,
      ..,
      .["recommended-typescript"],
      ()
        .()
        .(),
    ],
  },
);

The rule name is derived automatically from the function name (functionComponentDefinitionfunction-component-definition), and registered as @eslint-react/kit/function-component-definition at "error" severity.

API Reference

eslintReactKit (default export)

import eslintReactKit from "@eslint-react/kit";

eslintReactKit(): Builder

Creates a Builder instance for registering custom rules via the chainable .use() API.

RuleFunction

import type { RuleFunction } from "@eslint-react/kit";

type RuleFunction = (context: RuleContext, toolkit: RuleToolkit) => RuleListener;

A function that receives the ESLint rule context and the structured RuleToolkit toolkit, and returns a RuleListener (AST visitor object).

Rules are defined as named functions that return a RuleFunction. The function name is automatically converted to kebab-case and used as the rule name under the @eslint-react/kit plugin namespace.

// Function name `noForwardRef` → rule name `no-forward-ref`
// Registered as `@eslint-react/kit/no-forward-ref`
function noForwardRef(): RuleFunction {
  return (context, { is }) => ({ ... });
}

// Functions that accept options work the same way
function forbidElements({ forbidden }: ForbidElementsOptions): RuleFunction {
  return (context) => ({ ... });
}

Builder

interface Builder {
  use<F extends (...args: any[]) => RuleFunction>(factory: F, ...args: Parameters<F>): Builder;
  getConfig(): Linter.Config;
  getPlugin(): ESLint.Plugin;
}

A chainable builder for registering custom rules.

MethodDescription
useRegisters a rule factory. The rule name is kebabCase(factory.name). Options type is inferred from the factory signature.
getConfigReturns a Linter.Config with all registered rules enabled at "error" severity.
getPluginReturns an ESLint.Plugin containing the registered rules and plugin metadata.

getConfig

Returns a flat Linter.Config object with all registered rules set to "error". This is a convenience wrapper that calls getPlugin() internally and adds the plugin plus rule entries to the config.

eslintReactKit()
  .use(noForwardRef) // no-arg factory
  .use(version, "19") // factory with inferred options
  .getConfig();

getPlugin

Returns an ESLint.Plugin object containing the registered rules and plugin metadata (name and version). Use this when you need finer-grained control over how the plugin is integrated into your ESLint configuration.

eslint.config.ts
const kit = eslintReactKit()
  .use(noForwardRef)
  .use(version, "19");

// Retrieve the raw plugin object
const plugin = kit.getPlugin();

// Use it in a custom flat config with your own namespace and severity
export default [
  {
    files: ["**/*.{ts,tsx}"],
    plugins: {
      react: plugin,
    },
    rules: {
      "react/version": "error",
      "react/no-forward-ref": "error",
    },
  },
];

merge

import { merge } from "@eslint-react/kit";

merge(...listeners: RuleListener[]): RuleListener

Merges multiple RuleListener (visitor) objects into a single listener. When two or more listeners define the same visitor key, the handlers are chained and execute in order.

This is essential for combining a collector's visitor with your own inspection logic.

RuleToolkit — the toolkit object

The second argument passed to the RuleFunction function is a structured RuleToolkit object:

kit
├── collect            -> Semantic collectors (components, hooks)
├── is                 -> All predicates (component, hook, React API, import source)
├── ast                -> AST utilities (unwrap type expressions)
├── hint               -> Detection hint bit-flags
├── flag               -> Component characteristic bit-flags
├── settings           -> Normalized ESLint React settings

collect

Collector factories create a { query, visitor } pair. The visitor must be merged into your rule listener via merge(). After traversal completes, query.all(program) yields all detected semantic nodes.

MethodReturnsDescription
components(context, options?)CollectorWithContext<FunctionComponentSemanticNode>Detects function components. Options: { hint?: bigint, collectDisplayName?: boolean }
hooks(context)CollectorWithContext<HookSemanticNode>Detects custom hook definitions.

CollectorWithContext extends Collector with contextual queries:

QueryDescription
query.all(program)All collected semantic nodes in the file.

is

All predicates live under kit.is — organized into four sub-sections.

Component

PredicateSignatureDescription
componentDecl(node, hint) -> booleanWhether a function node is a component. (context pre-bound)
componentName(name) -> booleanStrict PascalCase component name check.
componentNameLoose(name) -> booleanLoose component name check.
componentWrapperCall(node) -> booleanWhether a node is a memo(…) or forwardRef(…) call. (context pre-bound)
componentWrapperCallback(node) -> booleanWhether a function is the callback passed to a wrapper. (context pre-bound)

Hook

General hook predicates:

PredicateSignatureDescription
hookDecl(node) -> booleanWhether a function node is a hook (by name).
hookCall(node) -> booleanWhether a node is a hook call.
hookName(name) -> booleanWhether a string matches the use[A-Z] convention.
useEffectLikeCall(node, additionalHooks?) -> booleanWhether a node is a useEffect/useLayoutEffect-like call.
useRefLikeCall(node, additionalHooks?) -> booleanWhether a node is a useRef-like call.
useStateLikeCall(node, additionalHooks?) -> booleanWhether a node is a useState-like call.
useEffectSetupCallback(node) -> booleanWhether a node is a useEffect setup function.
useEffectCleanupCallback(node) -> booleanWhether a node is a useEffect cleanup function.

React API

Factory functions (context pre-bound):

PredicateSignatureDescription
API(apiName) -> (node) -> booleanFactory: creates a predicate for a React API identifier. (context pre-bound)
APICall(apiName) -> (node) -> booleanFactory: creates a predicate for a React API call. (context pre-bound)

All React API predicates and factories have context pre-bound — no need to pass the rule context manually:

// Direct check
is.memoCall(node);

// Useful in filter/find
nodes.filter(is.memoCall);

// Factory for any API name
const isCreateRefCall = is.APICall("createRef");

// Returns true when the node is a call to `createRef`
isCreateRefCall(node);


ast

Low-level AST utilities for handling TypeScript-specific syntax.

MethodSignatureDescription
findParent(node, test) -> Node | nullWalks up the AST from the given node and returns the first ancestor that satisfies the predicate, stopping at Program. Returns null if no match is found.
unwrap(node) -> NodeRecursively strips TypeScript type-expression wrappers (TSAsExpression, TSSatisfiesExpression, TSNonNullExpression, TSTypeAssertion, TSInstantiationExpression) and ChainExpression from a node, returning the underlying value.

hint

Bit-flags that control what the component collector considers a "component". Combine with bitwise OR (|) and remove with bitwise AND-NOT (& ~).

// The default hint used when none is specified
hint.component.Default;

// All available flags
hint.component.DoNotIncludeFunctionDefinedAsObjectMethod;
hint.component.DoNotIncludeFunctionDefinedAsClassMethod;
hint.component.DoNotIncludeFunctionDefinedAsArrayMapCallback;
hint.component.DoNotIncludeFunctionDefinedAsArbitraryCallExpressionCallback;
// … and more (inherits all JsxDetectionHint flags)

Customization example:

const { query, visitor } = collect.components(context, {
  // Also treat object methods as components (remove the exclusion flag)
  hint: hint.component.Default & ~hint.component.DoNotIncludeFunctionDefinedAsObjectMethod,
});

flag

Bit-flags indicating component characteristics. Check with bitwise AND (&).

flag.component.None; // 0n — no flags
flag.component.Memo; // wrapped in React.memo
flag.component.ForwardRef; // wrapped in React.forwardRef

Usage:

for (const component of query.all(program)) {
  if (component.flag & flag.component.Memo) {
    // This component is memoized
  }
}

settings

Exposes the normalized react-x settings from the ESLint shared configuration (context.settings["react-x"]). This lets your custom rules read and react to the same project-level settings used by the built-in rules.

PropertyTypeDefaultDescription
versionstringauto-detectResolved React version (e.g. "19.2.7").
importSourcestring"react"The module React is imported from (e.g. "@pika/react").
compilationMode"infer" | "annotation" | "syntax" | "all" | "off""off"The React Compiler compilation mode the project uses.
polymorphicPropNamestring | null"as"Prop name used for polymorphic components.
additionalRefHooksRegExpLikePattern matching custom hooks treated as ref hooks.
additionalStateHooksRegExpLikePattern matching custom hooks treated as state hooks.
additionalEffectHooksRegExpLikePattern matching custom hooks treated as effect hooks.

RegExpLike is an object with a test(s: string) => boolean method (same interface as RegExp).

Usage:

import type {  } from "@eslint-react/kit";

function ( = "19"):  {
  return (, {  }) => ({
    () {
      if (!..(`${}.`)) {
        .({
          : ,
          : `This project requires React ${}, but detected version ${.}.`,
        });
      }
    },
  });
}

Examples

Resources

  • AST Explorer - A tool for exploring the abstract syntax tree (AST) of JavaScript code, which is essential for writing custom rules.
  • ESLint Developer Guide - Official ESLint documentation for creating custom rules.
  • Using the TypeScript Compiler API - TypeScript compiler API documentation for working with type information in custom rules.

On this page