Don’t do what you should do, do you want.

Why ESLint?


This is not an article comparing the pros and cons of ESLint and other related tools, but an introduction to why I choose to use ESLint for code checking and formatting. Although other tools will inevitably be mentioned, I believe that the existence of each popular tool is due to its own characteristics and advantages. As users, we only need to choose the tools that suit our needs and preferences. If there are issues with the tools, we can help improve them through feedback and contributions.

Advantages of ESLint that I value#

More flexible formatting#

Regarding why I don't use formatting tools like Prettier or dprint, I think Anthony Fu's article Why I don't use Prettier has already explained it clearly enough. Here, I would like to add my own thoughts.

Configuring printWidth does not tell Prettier when we want to break lines. By increasing the value of printWidth to avoid breaking a long string variable that should not be broken, you may end up not breaking the parameters of a multi-parameter function that you expect to be broken. The problem with // prettier-ignore is similar to // @ts-ignore. Although we successfully tell the tool not to break the line here, we also lose the other formatting rules that could have been applied here.

Although Prettier's philosophy is that users don't need to consider formatting configuration options and can confidently leave their code to it to make it beautiful, in reality, the existing configurable options may be one of the reasons why Prettier has become the most popular formatting tool in the JavaScript community. Imagine using a formatting tool that cannot control whether to use tab or space for indentation. Considering the tab vs. space debate is basically a 50-50 situation, if this option does not exist, Prettier would lose at least half of its users, right?

In addition, for options that are not so controversial, Prettier's inflexibility may cause some trouble for users. For example, Prettier enforces keeping an empty line at the end of a file, which is not configurable. Although this behavior is supported by most people and is beneficial for tools like Git, for those who cannot tolerate it, they have to find some hack or look for alternative tools to replace Prettier.

Yes, we have other formatting tools to choose from, such as dprint, which has better performance and provides more configuration options. However, it still does not solve the problem of when the code should be broken. This is not a bug, but a result of their own working mode. Moreover, even if dprint has more configuration options, you can never satisfy everyone's needs. In the world of ESLint, you can easily meet your own needs by configuring the options of existing rules or writing plugins.

When I noticed that the existing rules in ESLint Stylistic seemed to not handle the spacing between multiple parameters in JSX, all I needed to do was to put the current code in the TypeScript-ESLint playground and write a plugin based on the data structure displayed on the right side by ESTree to implement my own requirements.


By rewriting tools originally written in JavaScript using a more performant language, we can achieve better performance. However, this usually comes at a cost. Lint tools written in Rust, for example, generally cannot easily customize rules, so only rules that are extremely popular in the ESLint community can be ported. This means that we cannot have official ESLint plugins released at the first time, such as eslint-plugin-react-compiler released with React Compiler, or have niche but useful plugins in the ESLint community, such as ESLint Plugin Command.

In addition to extending rules, ESLint also supports extending the languages that can be linted. Now you can easily use ESLint for code checking in languages such as Vue, JSON, YAML, Toml, Markdown, Astro, Svelte, and more. However, for lint tools written in native languages, they usually only prioritize the most popular languages. For example, if you use Biome, you temporarily cannot use it when writing Vue projects and still need to fall back to ESLint. I don't like the inconsistency caused by using different tools in different projects.

Excellent ecosystem#

In this section, I don't want to mention how rich the plugin ecosystem of ESLint is again. Let's talk about the ESLint VSCode plugin. In addition to the automatic fix on save feature that we use every day, it also provides some other useful features.

When using ESLint, there are rules that we want to automatically fix but not immediately when saving. For example, removing unused imports or immediately changing let to const (we may reassign the variable soon). In this case, we can use the eslint.codeActionsOnSave.rules setting.

  "eslint.codeActionsOnSave.rules": [

With lint-staged and simple-git-hooks, we can ignore certain rules in the editor and automatically fix them before committing.

Another very useful setting is eslint.rules.customizations. We have disabled the automatic fix for some rules, but the editor still displays them as errors. With this setting, we can reduce the severity or completely disable these rules.

  "eslint.rules.customizations": [
    { "rule": "@stylistic/*", "severity": "off" },
    { "rule": "@stylistic/no-tabs", "severity": "default" },
    { "rule": "@stylistic/max-statements-per-line", "severity": "default" }
    { "rule": "antfu/consistent-list-newline", "severity": "off" },
    { "rule": "prefer-const", "severity": "off" },
    { "rule": "unused-imports/no-unused-imports", "severity": "off" },
    { "rule": "simple-import-sort/*", "severity": "off" },

This setting is very useful for using ESLint as a code formatting tool. We can directly disable the error display of rules in ESLint Stylistic in the editor while retaining their automatic fix functionality. In the next version, it also allows you to adjust the severity of all rules that can be automatically fixed.

Type-aware lint rules#

Lint tools based on Rust are fast, but they do not have the ability to lint based on type information. Josh Goldberg provides a detailed explanation in his article Rust-Based JavaScript Linters: Fast, But No Typed Linting Right Now.

oxlint recently made an attempt, but it seems to have brought it back to JavaScript speed.

Biome is preparing to implement Type-aware linter.

ESLint's "disadvantages" that I don't care about#


You can see many benchmarks showing that tools like oxlint and biome have much better performance than ESLint. However, from my use case, performance issues don't seem to be that important.

Real-time linting in the editor and linting during pre-commit usually only need to check a small number of files, and we can leave the complete linting process to the CI. CI does not block our local development process, and we only need to lint specific files locally when there is an error in CI.

The cases where I still encounter performance issues in the editor are when the project gradually becomes larger and enabling type-based rules causes noticeable delays in saving operations in the editor. But even in this case, we don't have to compromise completely and disable type-based rules. The flexibility of ESLint Flat Config allows us to disable specific rules in the editor. In the terminal or CI environment, we can still perform complete linting.

For my own ESLint Config, you can use the following configuration.

import defineConfig from "eslint-config-hyoban";

const isInEditor = !!(
  (process.env.VSCODE_PID ||
    process.env.VSCODE_CWD ||
    process.env.JETBRAINS_IDE ||
    process.env.VIM) &&

export default defineConfig({
  typeChecked: isInEditor ? false : "essential",

Unofficial recommendations#

Both ESLint and typescript-eslint have decided to deprecate formatting-related rules and do not recommend using ESLint for formatting. However, I don't think this is a problem. Deprecating these rules and transferring them to the community for maintenance is actually a good thing. We now have tools like ESLint Stylistic that are ready to use and perform very well.

Complex configuration, troublesome upgrades#

ESLint 9.0 recently made many people feel that major version upgrades of ESLint are very complex. The main issues encountered are the new configuration file format requiring rewriting of configurations and breaking changes in the API causing many plugins to not work in 9.0.

However, I think this is a temporary problem. The new configuration file brings many useful new tools and usages, which outweigh the disadvantages. For example, ESLint Config Inspector can help us write and test configuration files better; dynamic configuration generation based on project dependencies (enabling React hooks-related rules only in projects with React) is possible.

The problems caused by breaking changes in the API can also be solved in various ways:

  1. Write a PR to adapt upstream plugins to ESLint v9. In many cases, we only need to modify a few lines of code to use the new API in ESLint v9 and keep the old API for compatibility.
  2. Temporarily use ESLint v8 and wait for plugin compatibility (we can still use Flat Config).
  3. Use the official ESLint Compatibility Utilities to help with the upgrade.


It needs to be emphasized again that these are just my personal feelings and opinions, and there may be places where I haven't considered them properly. I welcome you to communicate with me and share your opinions.

If you want to try ESLint All In One now, I highly recommend starting with Anthony Fu's ESLint config, which supports a wide range of languages and frameworks, and you can also configure it flexibly based on it.

If you mainly write TypeScript and React, I also recommend trying my ESLint config. The philosophy of configuring rules is to use the rule presets provided by plugins as much as possible and adjust them according to my own habits. At the same time, it provides strict and typeChecked options for different levels of adjustments.

Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.