7 Recommended ESLint Rules for React TypeScript Project

The combination of ESLint and Prettier is Amazing. It not only reduces formatting time dramatically, but also saves your brain CPU. In addition, if you work as a team, it reduces review stress.

Normally, you might setup ESLint and Prettier based on presets like Airbnb, Google and Standard. But because their presets are not customized for your own project, there is still room for improvements.

So this article introduces 7 recommended ESLint configs which have a big impact on your React TypeScript Project!

If you don’t setup ESLint and Prettier, feel free to refer to my previous article.

Auto Format with ESLint and Prettier for React TypeScript Project

Auto formatting is a great enhancement of DX. It saves your development time and drastically increase your productivity…


1. arrow-body-style

Though arrow-body-style is a very simple rule, it has a huge impact when you write codes. The recommended setting of it is "arrow-body-style": ["error", "as-needed"] which force you to remove an unnecessary return like the below.// "arrow-body-style": ["error", "as-needed"],// Incorrect
let foo = () => {
   return 0;
};// Correct
let foo = () => 0

gif: auto format for arrow body style

2. react/self-closing-comp

react/self-closing-comp is similar to arrow-body-style. Although it’s simple, it reduces a large amount of time because there are so many chances to write self closing JSX.

The recommended setting is react/self-closing-comp: ["error", { "component": true, "html": true }]// "react/self-closing-comp": [
//   "error", {
//     "component": true,
//     "html": true
//   }
// ]// Incorrect
<Hello name="John"></Hello>;
<div className="content"></div>;// Correct
<Hello name="John" />
<div className="content" />

"component": true forces you to write a custom component with self closing tag. In addition, "html": true changes normal html tag to self closing one.

gif: auto format for self closing comp

3. autofix/no-unused-vars

This is originally from no-unused-vars rule of ESLint. Although original no-unused-vars doesn’t format automatically, I recommend using eslint-plugin-autofix. It formats automatically.$ yarn add -D eslint-plugin-autofix

After that, you can add it as ESLint plugin.{
 "plugins": ["react", "@typescript-eslint", "autofix"],
 "rules": {

Now, you can use define no-unused-vars rule like below !// "autofix/no-unused-vars": [
//   "error",
//   {
//     "argsIgnorePattern": "^_",
//     "ignoreRestSiblings": true,
//     "destructuredArrayIgnorePattern": "^_"
//   }
//  ]// Incorrect
function foo(x, y) {
   return x + 1;
foo();var { foo, ...coords } = data;const [a, b] = ["a", "b"];
console.log(b);// Correct
function foo(x, _y) {
   return x + 1;
foo();var { foo, ...coords } = data;const [_a, b] = ["a", "b"];

If you just add autofix/no-used-vars: "error" , all unused variable shows lint error. However, sometimes you want to define unused variables to show some intentions. So I recommend to use argsIgnorePattern: "^_" . With _ prefix, it’s allowed to use.

ignoreRestSiblings option destructuredArrayIgnorePattern are bit case specific rules.

Sometimes you would delete a key from object by using rest propery like below. In this situation, you might not use the removed key anymore. With ignoreRestSiblings , you can ignore the removed key from .const obj = { first: "John", last: "Lennon", age: 30 };
const { age, ...name } = obj;  // remove age from obj// => prevent age from ESLint error

The similar ESLint error happens in array destructuring. When you use array destructuring, you might not use the first destructured element. With destructuredArrayIgnorePattern: "^_" , you can prevent ESLint error.const [_A, B, C] = Promise.all([fetchA, fetchB, fetchC]);console.log(B, C);// => prevent A from ESLint error

4. @typescript-eslint/consistent-type-imports

When you import TypeScript types, it’s recommended to use type only import for better performance. But using normal and type only import properly bothers me a lot.import { useEffect } from "react;
import type { FC } from "react;

So it’s time to use @typescript-eslint/consistent-type-imports. It automatically detects if imported module is type or not and formats if needed.// "@typescript-eslint/consistent-type-imports": [
//   "error",
//   {
//     "prefer": "type-imports",
//   }  
// ],// Incorrect
import { useEffect, FC } from "react";// Correct
import { useEffect } from "react";
import type { FC } from "react";

gif: auto format for type only import

5. import/order

Actually, I like import/order rule the best. Because there are so many imports, organizing order of imports by yourself wastes a lot of time in total. So it’s time to use auto format !// "import/order": [
//   "error",
//   {        
//     "groups": [
//       "builtin",
//       "external",
//       "parent",
//       "sibling",
//       "index",
//       "object",  
//       "type"
//     ],
//     "pathGroups": [
//       {
//         "pattern": "@/**/**",
//         "group": "parent",
//         "position": "before"
//       }
//     ],
//     "alphabetize": { "order": "asc" }
//   }
// ],

The rule itself is bit complicated. groups key defines actual order between module types. The config above orders node built in modules (like path) before external packages (like react). If you want to know the detail of each group, please check the official docs.

pathGroups creates a custom group. I often defines it for import alias.// import alias
import foo from "../../src/path/to/foo"; // not readable
import foo from "@/src/path/to/foo";  // using import alias

With pathGroups setting, modules using import alias are ordered before parent .

alphabetize means order in same module. react is ordered before vue (of course it doesn’t happen).

6. no-restricted-imports

As I said above, import alias is very helpful. I hope you guys already use it. Once you introduce import alias, you might want to forbid to use relative import. So it’s time to set no-restricted-imports.// "no-restricted-imports": [
//   "error",
//   {
//     "patterns": ["../"]
//   }
// ],

It restricts to import parent module by relative path. On the other hand, it allows to import child module by relative path. It’s kind of preference. If you want to forbid any relative path, you can just add ./ to patterns .

Actually, it doesn’t format automatically because ESLint doesn’t know your custom import alias. So it should be done by hand.

7. react-hooks/exhaustive-deps

React Hook requires to define dependencies to detect when internal logic should be run. But if you have complicated logic in React Hook, it’s difficult to write all dependencies precisely. So I use react-hooks/exhaustive-deps to detect missing dependencies.

To use react-hooks ESLint rule, you need to add react-hooks package as a plugin. If it’s not done yet, please set it.// .eslintrcjson
 "plugins": [

The rule itself is very simple. Just add one line like below.// "react-hooks/exhaustive-deps": "error"

image: exhaustive-deps error

This article explains 7 recommended ESLint rules for TypeScript React project. Although the changes are very small, but it would speed up your development for a long time to come.