Skip to main content

What About Formatting?

We recommend against using ESLint for formatting. We recommend using Prettier, dprint, or an equivalent instead.

Formatters vs. Linters

Formatters are tools that verify and correct whitespace issues in code, such as spacing and newlines. Formatters typically run very quickly because they are only concerned with changing whitespace, not code logic or naming.

Linters are tools that verify and correct logical and non-whitespace style issues in code, such as naming consistency and bug detection. Linters often take seconds or more to run because they apply many logical rules to code.

Problems with Using Linters as Formatters

Linters are designed to run in a parse, check, report, fix cycle. This means that there is a lot of intermediate work that needs to be done before a linter can fix a formatting issue with your code.

Additionally linters typically run each rule isolated from one another. This has several problems with it such as:

  • any two lint rules can't share config meaning one lint rule's fixer might introduce a violation of another lint rule's fixer (eg one lint rule might use the incorrect indentation character).
  • lint rule fixers can conflict (apply to the same code range), forcing the linter to perform an additional cycle to attempt to apply a fixer to a clean set of code.

These problems cause a linter to be much slower - which can be much more of a problem in projects that enable typed linting. Formatting with a linter is also much less consistent and less able to handle edge-cases than a purpose-built formatter. The maintenance cost of formatting-related lint rules is typically very high as a result.

Modern formatters such as Prettier are architected in a way that applies formatting to all code regardless of original formatting. This design allows formatters to be much more comprehensive and consistent at much lower maintenance cost than linters.

Suggested Usage - Prettier

Neither typescript-eslint nor ESLint core enable any formatting-related rules in any recommended presets. However, some third party plugin configurations may still enable that bad practice.

If you see formatting rules enabled in your ESLint configuration, we recommend using eslint-config-prettier to disable formatting rules in your ESLint configuration. You can then configure your formatter separately from ESLint.

Using this config by adding it to the end of your extends:

eslint.config.js
// @ts-check

import eslint from '@eslint/js';
import someOtherConfig from 'eslint-config-other-configuration-that-enables-formatting-rules';
import prettierConfig from 'eslint-config-prettier';
import tseslint from 'typescript-eslint';

export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommended,
...someOtherConfig,
prettierConfig,
);

Note that even if you use a formatter other than prettier, you can use eslint-config-prettier as it exclusively turns off all formatting rules.

eslint-plugin-prettier

eslint-config-prettier is not the same as eslint-plugin-prettier.

  • The config only disables rules from core and other plugins.
  • The plugin loads and runs Prettier inside ESLint.

Running Prettier inside ESLint can be slow: see Performance Troubleshooting > eslint-plugin-prettier. However, because it doesn't re-implement Prettier's logic in ESLint, the caveats mentioned about using linters for formatting don't apply to eslint-plugin-prettier either.

ESLint Core and Formatting

Most lint rules fall into one of two to three categories:

  • Logical: Rules that care about the logic in runtime behavior of code (such as missing awaits or invalid logical checks).
  • Stylistic: Rules that care about style concerns which do impact runtime behavior of code, but generally not logic. These are mostly around naming or which roughly-equivalent syntax constructs to use (such as function declarations vs. arrow functions).
    • Formatting: Stylistic rules that care only about trivia (semicolons, whitespace, etc.) without impacting the runtime behavior of the code. These rules conflict with dedicated formatters such as Prettier.

Per ESLint's 2020 Changes to Rule Policies blog post, ESLint itself has moved away from stylistic rules, including formatting rules:

Stylistic rules are frozen - we won't be adding any more options to stylistic rules. We've learned that there's no way to satisfy everyone's personal preferences, and most of the rules already have a lot of difficult-to-understand options. Stylistic rules are those related to spacing, conventions, and generally anything that does not highlight an error or a better way to do something.

We mirror the ESLint team's move away from formatting and stylistic rules. With the exception of bug fixes, no new formatting- or stylistic-related pull requests will be accepted into typescript-eslint.

eslint-stylistic

The downside of using a comprehensive formatter for formatting is that it will strictly apply opinions to code. Although you can ignore code in Prettier and other formatters, including inline such as with // prettier-ignore comments, formatters are much more opinionated than lint rules.

The eslint-stylistic project provides an ESLint plugin containing formatting and stylistic rules. That plugin can serve as your formatter if you strongly prefer not to use a dedicated formatter.

See ESLint Stylistic > Why? for more details on that project's motivation, and ESLint Stylistic > Getting Started for how to set it up.