React Japan light logo
article hero
Steven Sacks
Steven Sacks

ESLint - Foundation

  • Updated 2025-08-26

The following are my recommendations for ESLint, Prettier, Stylelint, and editorconfig.

ESLint 9

Use the latest version of ESLint 9.

Plugins

These are the plugins with rulesets (with some customization) that will form the foundation of your ESLint setup. Some of these are optional/opinionated and are discussed in following articles.

Here is my recommended configuration in the eslint.config.mjs file. You can choose to modify the setting of the rules as you see fit, but I believe these rules should all be set to something.

Details

You can lookup any of the rules yourself and decide if you want to change the setting. Here are my justifications for some of them.

  • max-params - In functional programming, you should ideally only have 1 param per function. In practice, this can be a bit too restrictive, so having a limit of 3 params is sensible before converting to a single object with named params. Too many params requires developers to memorize the order and also makes multiple optional params a pain. It does not matter if you can look up the signature or TypeScript helps you. In practice, this limit is better. React components are a great example of this in practice because props is single object param.
  • @stylistic/quotes - This ensures optimal quotes for all 3 types (single, double, backtick).
  • react/jsx-boolean-value - Being explicit is better, and it makes props look consistent. Even for people who don’t prefer this, since it fixes on save, they can write it without being explicit, and when they save, it makes it explicit, and thus consistent for everyone.
  • @typescript-eslint/array-type - Pick the setting you prefer. There are good arguments for both array and generic. This enforces one or the other automatically and consistency is our goal. It automatically fixes to the preferred one on save, so developers can write whichever is more comfortable for them.
  • max-lines - If your components are getting too large, it’s usually a sign that you should break them up into smaller ones for performance, readability, and separation of concerns. There are going to be exceptions here and there. In those cases, the developer can disable this rule in the file, and it can be discussed during a code review. In my experience, nearly all of my component files are less than 200 lines, and the rule allows up to 300, by default.
  • @typescript-eslint/consistent-type-definitions - You should use type for functional programming and immutability. interface is for OOP and mutability. This article has a good explanation why. No matter what, you want to consistently use one or the other. The rule is disabled for .d.ts files because third party libraries may require interface.

.prettierrc

These are my Prettier settings.

{
  "bracketSpacing": false,
  "experimentalTernaries": true,
  "plugins": ["prettier-plugin-tailwindcss"], // if you use tailwind
  "singleQuote": true,
  "tabWidth": 2,
  "tailwindFunctions": ["twJoin", "twMerge"], // if you use tailwind
  "trailingComma": "es5"
}

bracketSpacing

Some people like bracketSpacing. I think it makes code wider than it needs to be, and I don’t find that it improves readability. If you like it, stick with the default setting.

experimentalTernaries

You can read about them here. These will eventually become the default, so I enable them to get used to them sooner. I agree with the Prettier team that they are better.

plugins

If you use Tailwind, you should use the Tailwind Prettier Plugin.

singleQuote

It is easier to type single quotes (does not require typing two keys at once), and whether it is more common to use apostrophes or double-quotes inside of strings, it does not outweigh how frequently you type single quotes while coding. Single quotes have been the standard for a very long time, and, in my experience, everyone sets this to true.

tabWidth

Set this to your preference. I used to prefer 4, but I've gotten used to 2, which is more popular these days.

tailwindFunctions

If you use Tailwind, you should use Tailwind Merge, instead of clsx or classnames.

trailingComma

Trailing commas are great! But, I don’t like them in function parameters. I think they look messy before the closing parentheses and arrow const foo = (a, b, c,) =>. So, I set this to “es5” instead of “all”.

.stylelintrc.json

Here are my suggested settings for Stylelint.

{
  "extends": [
    "stylelint-config-standard",
    "stylelint-config-idiomatic-order",
    "stylelint-config-tailwindcss"
  ],
  "ignoreFiles": ["public/build/**/*.css"],
  "overrides": [
    {
      "files": ["**/*.module.css"],
      "rules": {
        "selector-class-pattern": "^[a-z][a-zA-Z0-9]+$"
      }
    }
  ],
  "plugins": ["stylelint-order"],
  "rules": {
    "at-rule-no-deprecated": null,
    "no-descending-specificity": null,
    "selector-pseudo-class-no-unknown": [
      true,
      {
        "ignorePseudoClasses": ["global", "local"]
      }
    ]
  }
}
 

.editorconfig

IDEs will automatically follow your rules by adding this file to the root of your project. These are my suggested settings.

# editorconfig.org
 
root = true
 
# We recommend to keep these unchanged
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
 
[*.md]
trim_trailing_whitespace = false

Next article: ESLint - Sorting

Main article: Take your workflow to the next level with ESLint fix on save