12 essential ESLint rules for React
Tags: react, webdev
Published: 2022-08-23 19:37:18

**Written by [Joe Attardi](https://blog.logrocket.com/author/joeattardi/)✏️**

## Introduction

ESLint has a comprehensive set of rules for JavaScript code that cover stylistic choices and prevent common bugs. Using ESLint alone will give your project a boost, but there are ESLint plugins available to add React-specific rules that will help you write solid React applications. 

In this post, we’ll go over these ESLint rules and plugins, including as they apply to Hooks. Here are some quick links for you to jump around:

*   [React Hooks rules (`eslint-plugin-react-hooks`)](#react-hooks-rules)
    *   [`react-hooks/rules-of-hooks`](#react-hooks-rules-hooks)
    *   [`react-hooks/exhaustive-deps`](#react-hooks-exhaustive-deps)
*   [React rules (`eslint-plugin-react`)](#react-rules)
    *   [`react/button-has-type`](#react-button-has-type)
    *   [`react/prop-types`](#react-prop-types)
    *   [`react/require-default-props`](#react-require-default-props)
    *   [`react/no-array-index-key`](#react-no-array-index-key)
    *   [`react/react-in-jsx-scope`](#react-in-jsx-scope)
    *   [`react/jsx-uses-react`](#react-jsx-uses-react)
    *   [`react/display-name`](#react-display-name)
    *   [`react/no-danger-with-children`](#react-no-danger-with-children)
    *   [`react/jsx-no-bind`](#react-jsx-no-bind)

## React Hooks rules (`eslint-plugin-react-hooks`) <a name="react-hooks-rules">

This plugin only contains two rules, but they are critical to avoiding common pitfalls when writing [function components](https://blog.logrocket.com/what-are-react-pure-functional-components/) with Hooks.

### react-hooks/rules-of-hooks <a name="react-hooks-rules-hooks">

This rule enforces that components follow the [Rules of Hooks](https://reactjs.org/docs/hooks-rules.html) when using Hooks. The rules are discussed in detail in the [React documentation](https://reactjs.org/docs/hooks-rules.html#explanation), but there are two rules that must be followed when using Hooks:

1.  Hooks should only be called from the top-level code of your component. What this really means is that the Hooks should not be called conditionally — they should instead be called on every render, in the same order, to avoid issues and subtle bugs
2.  Hooks should only be called from either a function component, or another Hook
    1.  Custom Hooks often compose behavior together from inbuilt, or even other custom, Hooks

In the default configuration, violations of this rule will cause an error, causing the lint check to fail.

### react-hooks/exhaustive-deps <a name="react-hooks-exhaustive-deps">

This rule enforces certain rules about the contents of the [dependency array](https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects) that is passed to Hooks, such as `useEffect`, `useCallback`, and `useMemo`. In general, any value referenced in the effect, callback, or memoized value calculation must be included in the dependency array. If this is not done properly, issues such as out-of-date state data or [infinite rendering loops](https://blog.logrocket.com/solve-react-useeffect-hook-infinite-loop-patterns/) can result. 

This rule is good at finding potential dependency-related bugs, but there are some limitations:

*   Custom Hooks with dependency arrays will not be checked with this rule. It only applies to the inbuilt Hooks
*   The rule can only properly check dependencies if it is a static array of values. If a reference to another array is used, or another array is spread into it, the rule will emit a warning that it cannot determine the dependencies

This rule has been somewhat controversial; there are several [long issue threads on GitHub](https://github.com/facebook/react/issues?q=is%3Aissue+exhaustive-deps+is%3Aclosed+sort%3Acomments-desc), but the React team has been good about soliciting and incorporating feedback. In the default configuration, violations of this rule are treated as warnings. 

The details of this rule could take up an entire article on their own. For a deeper dive on this rule and how to properly use it, see the [Understanding the React exhaustive-deps linting warning](https://blog.logrocket.com/understanding-react-exhaustive-deps-linting-warning) article, here on the LogRocket blog.

## React rules (`eslint-plugin-react`) <a name="react-rules">

This plugin contains a lot more rules (100 rules at time of writing) that are specific to the core of React. Most rules cover general React practices, and others cover issues related to JSX syntax. Let's take a look at some of the more useful ones.

### react/button-has-type <a name="react-button-has-type">

For accessibility reasons, most clickable elements in a component that aren't simple links to another URL [should be implemented as buttons](https://blog.logrocket.com/building-accessible-glossary-vanilla-javascript/). A common mistake is to omit the `type` attribute from these buttons when they aren't being used to submit a form. 

When no `type` is specified, a button defaults to a type of `submit`. This can cause issues for buttons that descend from a `form` element. Clicking such a button inside of a form will cause a potentially unwanted form submission. 

Action buttons that are not intended to submit a form should have a `type` attribute of `button`. 

This rule enforces that all buttons explicitly have a `type` attribute — even ones that are intended as **Submit** buttons. By being explicit, unintentional submissions are avoided and the intent of the code is clear.

### react/prop-types <a name="react-prop-types">

Requires that all React components have their props described in a [`PropTypes`](https://reactjs.org/docs/typechecking-with-proptypes.html) declaration. These checks only throw errors in development mode but can help catch bugs arising from the wrong props being passed to a component. 

If your project uses TypeScript, this rule is also satisfied by adding a type annotation to the component props that describes them. 

These two approaches are covered in detail in [Comparing TypeScript and PropTypes in React applications](https://blog.logrocket.com/comparing-typescript-and-proptypes-in-react-applications/) by Dillion Megida.

### react/require-default-props <a name="react-require-default-props">

Depending on the component, some props may be required while others are optional. If an optional prop is not passed to a component, it will be `undefined`. This may be expected but can introduce bugs if the value is not checked. 

This rule requires that every optional prop is given a default value inside of a `defaultProps` declaration for the component. This default value can be explicitly set to `null` or `undefined` if that is what the component expects. 

With function components, there are two different strategies that can be used to check default props:

#### defaultProps 

This strategy expects the function component to have a `defaultProps` object with the defaults.

```javascript
const MyComponent = ({ action }) => { ... }

MyComponent.propTypes = {
  Action: PropTypes.string;
};

MyComponent.defaultProps = {
  action: 'init'
};
```

#### defaultArguments

This strategy expects the defaults to be specified in the function declaration, using JavaScript's inbuilt default values syntax.

```javascript
const MyComponent = ({ action = 'init' }) => { ... }

MyComponent.propTypes = {
  Action: PropTypes.string;
};
```

If you use the `defaultArguments` strategy, there should not be a `defaultProps` object. If there is, this rule will fail.

### react/no-array-index-key <a name="react-no-array-index-key">

When rendering a list of items in React, we typically call `map` on an array, and the mapping function returns a component. To keep track of each item in the list, React needs these components to have a `key` prop. 

A common pitfall with rendering lists is using the array index as the key. This can cause unnecessary or even incorrect renders. The React documentation [advises against this practice](https://reactjs.org/docs/lists-and-keys.html#keys) due to the issues it can cause (there is also a [more detailed discussion about how keys are used](https://reactjs.org/docs/reconciliation.html#recursing-on-children)). A key is expected to be a unique identifier for that item, within the list, that does not change, like the primary key value in a database row. 

This rule ensures that the array index is not used as the key.

### react/react-in-jsx-scope <a name="react-in-jsx-scope">

Consider this simple React component:

```javascript
const Greeter = ({ name }) => <div>Hello {name}!</div>;
```

The `React` object is not referenced at all. However, `React` still needs to be imported or else you will encounter an error. This is due to the transpilation process of JSX. Browsers don't understand JSX, so during the build process (usually with a tool such as Babel or TypeScript), the JSX elements are transformed into valid JavaScript. 

This generated JavaScript code calls `React.createElement` in place of JSX elements. The above component might be transpiled to something like this:

```javascript
const Greeter = ({ name }) => React.createElement("div", null, "Hello ", name, "!");
```

The references to `React` here are why `React` must still be imported. This rule ensures that all files with JSX markup (not necessarily even a React component) have `React` in scope (typically through an `import` or `require` call).

### react/jsx-uses-react <a name="react-jsx-uses-react">

Always importing React is necessary for proper transpilation, but when ESLint looks at the file, it's still JSX, so it won't see `React` referenced anywhere. If the project is using the `no-unused-vars` rule, this results in an error since `React` is imported but not used anywhere. 

This rule catches this situation and prevents `no-unused-vars` from failing on the `React` import.

### react/display-name <a name="react-display-name">

For proper debugging output, all React components should have a display name. In many cases, this won't require any extra code. If a component is a named function, the display name will be the name of the function. In the below examples, the display name of the component will be `MyComponent`.

*   `const MyComponent = () => { … }`
*   `const MyComponent = function() { return …; }`
*   `export default function MyComponent() { return …; }`

There are some cases where the automatic display name is lost. This is typically when the component declaration is wrapped by another function or higher order component, like in the two examples below:

*   `const MyComponent = React.memo(() => { … });`
*   `const MyComponent = React.forwardRef((props, ref) => { … });`

The `MyComponent` name is bound to the new "outer" component returned by `memo` and `forwardRef`. The component itself now has no display name, which will cause this rule to fail. 

When these cases arise, a display name can be manually specified via the `displayName` property to satisfy the rule:

```javascript
const MyComponent = React.memo(() => { ... });
MyComponent.displayName = 'MyComponent';
```

### react/no-children-prop

React components accept a special prop called `children`. The value of this prop will be whatever content is inside the opening and closing tags of the element. Consider this simple `MyList` component:

```javascript
const MyList = ({ children }) => {
  return <ul>{children}</ul>;
};
```

This will render the outer `ul` element, and any children we put inside the element will be rendered inside of it.

```javascript
<MyList>
  <li>item1</li>
  <li>item2</li>
</MyList>
```

This is the preferred pattern with React components. It is possible, though not recommended, to pass children as an explicit children prop:

```javascript
<MyList children={<li>item1</li><li>item2</li>} />
```

The above usage will actually cause an error because JSX expressions, like the one passed as the explicit children prop, must have a single root element. This requires the children to be wrapped in a fragment:

```javascript
<MyList children={<><li>item1</li><li>item2</li></>} />
```

As shown in the first example, children are passed as child elements to the component directly, so the component is the root element of the expression. No fragment or other enclosing element is needed here. 

This is mainly a stylistic choice/pattern, but it does prevent inadvertently passing both an explicit `children` prop and child elements:

```javascript
<MyList children={<><li>item1</li><li>item2</li></>}>
  <li>item3</li>
  <li>item4</li>
</MyList>
```

In this case, the child elements (`item3` and `item4`) would be displayed, but `item1` and `item2` would not. This rule ensures that children are only passed in the idiomatic way, as child JSX elements.

### react/no-danger-with-children <a name="react-no-danger-with-children">

React's `dangerouslySetInnerHTML` prop allows arbitrary markup to be set as the `innerHTML` property of an element. This is generally not recommended, as it can expose your application to a cross-site scripting (XSS) attack. However, if you know you can trust the input and the use case requires it, this approach may become necessary. 

The prop expects an object with an `__html` property, whose value is a raw HTML string. This string will be set as the `innerHTML`. 

Because this replaces any existing child content, it doesn't make sense to use this in combination with a `children` prop. In fact, React will throw an error if you attempt to do this. Unlike some errors that only appear in development mode (like `PropTypes` validation errors), this error will crash your app. 

This rule enforces the same rule. If `dangerouslySetInnerHTML` is used with children, the lint rule will fail. It's much better to catch these errors when linting, or at build time, rather than reported by users once the app is deployed!

### react/jsx-no-bind <a name="react-jsx-no-bind">

Every time a React component is rendered, it comes at a performance cost. Oftentimes, certain patterns or practices can cause a component to unnecessarily re-render itself. There are many causes for this behavior, and this rule helps prevent one of them. 

When any function is defined inside the component, it will be a new function object on every render. This means that whenever the component is re-rendered, the prop is considered changed. Even with `React.memo`, the component will re-render. 

If the child component has any `useEffect` calls that take that function as a dependency, this can cause the effect to run again, creating the potential for an infinite loop that will likely freeze the browser. 

With this rule enabled, any function that is passed as a prop will be flagged. 

There are two ways this can be addressed. If the function does not depend on anything else inside the component, it can be moved outside of the component, where it is just a plain function that will always be the same memory reference. This ensures that the same function is passed to the prop each time. 

For cases where the function _does_ depend on the component in some way, the usual fix for this is to memoize it with the [`useCallback`](https://reactjs.org/docs/hooks-reference.html#usecallback) Hook. Any properties referenced in the function will have to be included in the `useCallback` dependency array; sometimes this requires multiple levels of memoization of values or functions. 

This adds some complexity, but has the benefit of helping to reduce extra renders and prevent infinite loops.

## Wrapping up

The rules covered here are just a few of the ones provided by the `eslint-plugin-react` plugin. Some rules can be opinionated or overzealous, but most also have configuration options to make them less strict. 

There is also another very helpful ESLint plugin centered around JSX and accessibility practices: [`eslint-plugin-jsx-a11y`](https://www.npmjs.com/package/eslint-plugin-jsx-a11y). The rules in this plugin check your JSX markup to make sure that good HTML accessibility practices are being followed. 

These React ESLint plugins can be helpful to avoid common pitfalls, especially if you’re still new to React. You can even write your own rules and plugins to cover other situations!

---

## Full visibility into production React apps

Debugging React applications can be difficult, especially when users experience issues that are hard to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, [try LogRocket](https://www2.logrocket.com/react-performance-monitoring). 

[![LogRocket signup](https://blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png)](https://www2.logrocket.com/react-performance-monitoring)

[LogRocket](https://www2.logrocket.com/react-performance-monitoring) is like a DVR for web and mobile apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.