Josh Goldberg
Cat rolled up in a circular ball within a fluffy white circular pet bed.

You Probably Don't Need eslint-config-prettier or eslint-plugin-prettier

Jan 23, 202415 minute read

Explaining two popular Prettier integrations for ESLint, and why I avoid both of them.

I’ve been proclaiming loudly for several years now to “STOP USING ESLINT FOR FORMATTING”. My belief is that formatters such as Prettier and linters such as ESLint are fundamentally different tools built for different purposes. While you can use ESLint for formatting thanks to ESLint Stylistic, ESLint recommends using a separate dedicated formatter and typescript-eslint also recommends against using ESLint for formatting.

The following two tools have historically been used to help ESLint interact nicely with Prettier:

I believe neither tool is useful in most projects anymore. This blog post will explain the purpose of each of them, their differences, and why I generally don’t use either.

Recap: ESLint Customizations

ESLint works by letting individually configure “rules”, or checks on your codebase. ESLint will do the work of parsing your code into a format rules understand, passing the code to those rules, and letting you know about any reports emitted by those rules.

ESLint is highly extensible: meaning you can customize many aspects of how it runs. The most common ways to customize it are:

Note that plugins and shareable configs are two different things.

ESLint Customization Example: TypeScript

If you use ESLint to lint TypeScript code then you’re hopefully using all three customizations:

Note that the typescript-eslint shareable configs come from the @typescript-eslint/eslint-plugin npm package. Hence the plugin: prefix in front of them: that’s how ESLint knows where to find the configs.

eslint-config-prettier

eslint-config-prettier is a shareable config that disables formatting-related rules. You can load it into your project by listing it under "extends" array in your ESLint config:

{
	"extends": [
		// (shorthand for "eslint-config-prettier")
		"prettier"
	]
}

The sole purpose of eslint-config-prettier is to turn rules off. On the inside, it looks like an object with a bunch of properties whose values are 0 or "off". Roughly:

{
	"curly": "off",
	"no-unexpected-multiline": "off",
	"@babel/object-curly-spacing": "off",
	"@typescript-eslint/lines-around-comment": "off"
}

Why eslint-config-prettier Came To Be

Back in the day, popular shareable configs such as eslint-config-airbnb were often used to enable many rules at once. These configs were popular because they established a well-known, opinionated style guide and set of logical checks on code. Their downside was that they were oftentimes too opinionated - even enabling formatting rules.

Developers got around those formatting rules by knowing that ESLint evaluates configs in the order they’re listed under "extends". eslint-config-prettier could be placed last in a project’s ESLint config to turn off any formatting rules enabled by previous plugins.

{
	"extends": [
		// 1. Configures many ESLint rules, including enabling some formatting ones
		"airbnb",
		// 2. Disables only the formatting rules from previous configs
		"prettier"
	]
}

By extending from eslint-config-prettier last, projects could gain the benefits of those popular shareable configs without having to run formatting rules within ESLint.

Why eslint-config-prettier Is Often Unnecessary

Over the last few years, ESLint best practices have advanced in two ways (among others):

As a result, many new projects haven’t felt a need to load in opinionated configs such as eslint-config-airbnb. Many projects start out with a much more straightforward set of configs:

  1. To start: "eslint:recommended", ESLint’s built-in recommended configuration
  2. If using TypeScript: "plugin:@typescript-eslint/recommended" or "plugin:@typescript-eslint/recommended-type-checked" for the recommended TypeScript rules
  3. Any framework- or library-specific plugins, such as eslint-plugin-jsx-a11y’s "plugin:jsx-a11y/recommended"

If you don’t use a legacy ESLint shareable config that enables formatting rules, you probably don’t need eslint-config-prettier. Adding eslint-config-prettier at the end of the "extends" list doesn’t do anything if nothing enabled formatting rules to begin with. Therefore, most projects don’t gain any benefit from eslint-config-prettier.

Furthermore, two insidious points of confusion can arise from using eslint-config-prettier redundantly:

I now recommend against including eslint-config-prettier in most new projects.

💡 Not sure whether you can safely remove "prettier" from your "extends"? Try removing it, then running npx eslint-config-prettier some/file.js to see if it points out any conflicting rules. Running ESLint with --print-config can alternately print out the full list of lint rule configurations for a file.

eslint-plugin-prettier

eslint-plugin-prettier is an ESLint plugin that provides two things:

For example, in ESLint’s legacy config format, you might enable it by extending its recommended config:

{
	"extends": ["plugin:prettier/recommended"]
}

Extending that config:

  1. Adds eslint-plugin-prettier to the "plugins" list of extended plugins, thereby loading in the prettier/prettier rule
  2. Enables the prettier/prettier rule
  3. Adds eslint-config-prettier to the "extends" list of extended configs

The advantage of this approach is that you don’t need to separately configure Prettier alongside ESLint. You can have one file -your ESLint config- that enables both.

Why eslint-plugin-prettier Is Often Harmful

There are two big problems with running Prettier inside an ESLint rule the way eslint-plugin-prettier does:

The performance point can become quite bad in projects that utilize type-checked rules.

😬 Keep in mind that lint rules don’t have visibility into formatting settings. Their auto-fixers aren’t likely to produce code that aligns to your formatter.

Type-checked linting is by nature generally at least as slow as running the TypeScript type checker on all linted files. Rust-Based JavaScript Linters: Fast, But No Typed Linting Right Now explains why. Running linting additional extra times adds up - and contributes to false negative perceptions around ESLint’s and typescript-eslint’s performance.

I strongly recommend you do not use eslint-plugin-prettier. We even explicitly recommend against eslint-plugin-prettier in the typescript-eslint formatting FAQs and typescript-eslint performance troubleshooting docs.

If prettier/prettier is enabled in your ESLint configuration, the best step you can take is to remove it and uninstall the eslint-plugin-prettier package altogether. Failing that, you can disable the rule manually ("prettier/prettier": "off" under "rules"). At that point you’ll still have the eslint-config-prettier shareable config enabled.

In Conclusion

Formatting and linting are two separate concerns. Mixing the two can have negative impacts on the performance and understandability of your developer tooling. My standard repository template, create-typescript-app, keeps the two explicitly separate.

If your ESLint configuration references eslint-config-prettier, I suggest you try to remove it from your config. You probably don’t need it anymore.

If your ESLint configuration references eslint-plugin-prettier, I strongly recommend you instead enable Prettier separately from ESLint. Running the prettier/prettier rule can introduce significant performance pain to your project.

No matter what tooling your ESLint configuration enables, if you haven’t overhauled it in a few years, I strongly recommend:

  1. Making sure "eslint:recommended" is in your rule extensions
  2. If you’re using TypeScript:
    1. Making sure at least plugin:@typescript-eslint/recommended is enabled - or even better, plugin:@typescript-eslint/recommended-type-checked
    2. Checking the .eslintrc.cjs from create-typescript-app, for any rules or plugins generally applicable to your project
  3. Checking github.com/dustinspecker/awesome-eslint for any additional plugins relevant to your project

💡 Configuring ESLint, Prettier, and TypeScript Together is a blog post of mine that goes more into how to configure these tools.

Happy linting, everyone! 🎉

Acknowledgements

Much appreciation to Anisha Malde for suggesting I write an explainer in this area and helping ideate contents. Thanks! 🙌

Thanks to Ben Scott, a maintainer of eslint-config-prettier, for reviewing the post and suggesting clarifications and corrections on how eslint-plugin-prettier is described.

Thanks to Simon Lydell, the original creator and a long-time maintainer of eslint-config-prettier, for reviewing the post and suggesting the npx eslint-config-prettier approach.

We should also note that although I personally disagree with using eslint-config-prettier and eslint-plugin-prettier now, they have been legitimately useful tools for many years. I like to think that they’ve helped many folks onboard to formatting and/or linting their code. Heck, I’d still rather work in a project that uses them than a project that doesn’t auto-format at all! Many thanks to all their contributors and maintainers over the years! ❤️


Liked this post? Thanks! Let the world know: