logoESLint React
Recipes

function-component-definition

Enforces arrow function style for function component definitions.

Overview

This rule enforces that function components are defined with arrow functions instead of function declarations or function expressions. It provides an auto-fix suggestion that converts non-arrow function components to arrow function syntax.

Rule Definition

Copy the following rule definition into your project (e.g. eslint.config.rules.ts):

eslint.config.rules.ts
import type { RuleDefinition } from "@eslint-react/kit";
import { merge } from "@eslint-react/kit";

/** Enforce arrow function style for function component definitions. */
export function functionComponentDefinition(): RuleDefinition {
  return (context, { collect }) => {
    const { query, visitor } = collect.components(context, {
      hint: hint.component.Default & ~hint.component.DoNotIncludeFunctionDefinedAsObjectMethod,
    });
    return merge(
      visitor,
      {
        "Program:exit"(program) {
          for (const { node } of query.all(program)) {
            if (node.type === "ArrowFunctionExpression") continue;
            context.report({
              node,
              message: "Function components must be defined with arrow functions.",
              suggest: [
                {
                  desc: "Convert to arrow function.",
                  fix(fixer) {
                    const src = context.sourceCode;
                    if (node.generator) return null;
                    const prefix = node.async ? "async " : "";
                    const typeParams = node.typeParameters ? src.getText(node.typeParameters) : "";
                    const params = `(${node.params.map((p) => src.getText(p)).join(", ")})`;
                    const returnType = node.returnType ? src.getText(node.returnType) : "";
                    const body = src.getText(node.body);

                    // function Foo(params) { ... } -> const Foo = (params) => { ... };
                    if (node.type === "FunctionDeclaration" && node.id) {
                      return fixer.replaceText(
                        node,
                        `const ${node.id.name} = ${prefix}${typeParams}${params}${returnType} => ${body};`,
                      );
                    }

                    // const Foo = function(params) { ... } -> const Foo = (params) => { ... }
                    if (node.type === "FunctionExpression" && node.parent.type === "VariableDeclarator") {
                      return fixer.replaceText(node, `${prefix}${typeParams}${params}${returnType} => ${body}`);
                    }

                    // { Foo(params) { ... } } -> { Foo: (params) => { ... } }
                    if (node.type === "FunctionExpression" && node.parent.type === "Property") {
                      return fixer.replaceText(
                        node.parent,
                        `${src.getText(node.parent.key)}: ${prefix}${typeParams}${params}${returnType} => ${body}`,
                      );
                    }

                    return null;
                  },
                },
              ],
            });
          }
        },
      },
    );
  };
}
eslint.config.ts
import eslintReactKit from "@eslint-react/kit";
import { functionComponentDefinition } from "./eslint.config.rules";

export default [
  // ... other configs
  {
    ...eslintReactKit()
      .use(functionComponentDefinition)
      .getConfig(),
    files: ["src/**/*.tsx"],
  },
];

Invalid

// Function declaration — will be reported
function MyComponent({ name }: { name: string }) {
  return <div>Hello, {name}!</div>;
}
// Function expression — will be reported
const MyComponent = function({ name }: { name: string }) {
  return <div>Hello, {name}!</div>;
};

Valid

// Arrow function — OK
const MyComponent = ({ name }: { name: string }) => {
  return <div>Hello, {name}!</div>;
};

How It Works

This rule uses the collect.components collector from @eslint-react/kit to find all function component definitions in the file. On Program:exit, it iterates through every detected component and reports any that are not ArrowFunctionExpression nodes.

The suggested fix handles three cases:

  1. Function declarationsfunction Foo() {}const Foo = () => {};
  2. Function expressions in variable declaratorsconst Foo = function() {}const Foo = () => {}
  3. Function expressions in object properties{ Foo() {} }{ Foo: () => {} }

The fix also preserves async, type parameters, parameter types, and return types.


See Also

On this page