# prettier-eslint: running Prettier first, then eslint --fix

> prettier-eslint is a Node module that pipes source text through prettier and then through eslint --fix, so ESLint rules can override Prettier's opinionated formatting. It suits projects with an existing ESLint config they do not want to give up.

**prettier/prettier-eslint** — Code :arrow_right: `prettier` :arrow_right: `eslint --fix` :arrow_right: Formatted Code :sparkles:

- Repository: https://github.com/prettier/prettier-eslint
- Website: https://opencollective.com/prettier-eslint
- Stars: 4,100 · Forks: 170
- Language: TypeScript
- License: MIT
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/prettier-prettier-eslint

## What prettier-eslint reconciles between Prettier and ESLint

The README states the problem plainly: ESLint's fix feature can auto-format much of your code according to your ESLint config, and Prettier is a more powerful automatic formatter, but Prettier is not opinionated enough, or its opinions differ from the author's. The result of running Prettier alone is a file that still trips lint rules. prettier-eslint exists to close that gap by running both, in a fixed order.

The audience is narrow and specific. You need an existing ESLint configuration that you are not willing to abandon, and you want Prettier's formatting as the base layer underneath it. If you have no ESLint rules that conflict with Prettier, this module adds a second formatter pass for nothing. The README also notes an exception: for files with an extension of .css, .less, .scss, or .json, only prettier runs, because eslint cannot process those.

## The prettier then eslint --fix pipeline and its option inference

The technical summary in the README is a single line: code goes to prettier, then to eslint --fix, then out as formatted code. That is the whole mechanism. There is no AST merging and no rule-by-rule arbitration; it is two formatters run in sequence over text.

What makes the ordering work is option inference. If you do not pass prettierOptions, prettier-eslint derives them from the eslintConfig, whether you supplied that config directly or it was found via filePath. You can supply some Prettier options and let the rest be inferred, which the README calls useful for options like parser. There is a second layer, fallbackPrettierOptions, which applies only when the corresponding ESLint rule cannot be found and you have not set the Prettier option manually. Without a fallback, the default Prettier value is used in that case. Note the README's emphasis: prettierOptions override the ESLint config, and fallbackPrettierOptions are the narrower escape hatch.

prettierLast reverses the pipeline. Instead of prettier then eslint --fix, it runs eslint --fix first and prettier second, so ESLint finds bugs and bad practices while Prettier enforces style. That is a different division of labour, and the README frames it as an alternative approach rather than a better one. The two settings produce different output on the same input; nothing in the README says one is a superset of the other.

Module resolution is also part of the design. By default prettier-eslint finds the relevant eslint and prettier based on filePath, falling back to its own installed copies if it cannot find them. eslintPath and prettierPath let you point at a specific module instead. This matters in monorepos where the file being formatted lives far from the tooling.

## Installing prettier-eslint and formatting a first string

The README distributes the module via npm, which ships with Node, and says to install it as a devDependency:

```bash
npm install --save-dev prettier-eslint
```

The package.json sets engines to ^20.19.0 || ^22.13.0 || >=24, so check your Node version before anything else. The package is type module and its exports map points at lib/index.js with types at lib/index.d.ts.

The README's usage example passes a source string, an eslintConfig, and prettierOptions. The original text has no semicolon, the ESLint rule semi is set to never, and the formatted result comes back as const { foo } = bar. In the README the call is awaited, so treat the return value as a promise.

```js
const format = require('prettier-eslint');

const sourceCode = 'const {foo} = bar';

const options = {
  text: sourceCode,
  eslintConfig: {
    parserOptions: { ecmaVersion: 7 },
    rules: { semi: ['error', 'never'] },
  },
  prettierOptions: { bracketSpacing: true },
  fallbackPrettierOptions: { singleQuote: false },
};

const formatted = await format(options);
```

For a real file, pass filePath instead of hand-writing eslintConfig, and let prettier-eslint locate the config for that path. If you want the formatted string plus the ESLint messages that were produced, use the analyze export, which the README describes as identical to format except that it returns output and messages.

```js
const { analyze } = require('prettier-eslint');

const result = await analyze({
  text: 'var x = 0;',
  eslintConfig: { rules: { 'no-var': 'error' } },
});
console.log(result.messages);
```

The README shows the console output for that call as an array with ruleId no-var, severity 2, and the message "Unexpected var, use let or const instead." If you see that array, the ESLint pass ran and reported. If output is unchanged and messages is empty, your rules probably did not match the text.

## Only parsing errors propagate, and analyze is the workaround

The throws section is the sharpest limitation in the README. prettier-eslint only propagates parsing errors when either prettier or eslint fails. It also logs a message saying what it was doing at the time of the failure. Beyond that, format will not show any message regarding broken rules in either prettier or eslint.

So a rule that fires but is not a parse failure is invisible to format. You get formatted text back and no signal that ESLint objected to something. That is why analyze exists: same formatting, but the messages array comes with it. If your use case is a CI check that must fail on a lint rule, calling format and inspecting the string is the wrong tool. Call analyze and read messages.

There is a second boundary worth naming. The README's own framing is that ESLint's fix feature and Prettier overlap, and that some Prettier opinions differ from the author's. This module is a reconciliation layer for that overlap, not a replacement for either tool. If your rules and Prettier's output already agree, the second pass is cost without benefit.

## prettier-eslint compared with eslint-config-prettier and eslint-plugin-prettier

The common alternative approach is the config-and-plugin route rather than a runtime pipeline. eslint-config-prettier turns off ESLint rules that would conflict with Prettier, and eslint-plugin-prettier runs Prettier as an ESLint rule. In that model ESLint remains the single entry point, and Prettier becomes one rule among many.

prettier-eslint inverts that. Prettier runs as its own formatter, then eslint --fix runs over the result, so ESLint rules can override Prettier's output rather than being disabled. That is a real difference in approach: config-and-plugin removes the conflict by switching rules off, while prettier-eslint resolves it by running both and letting the second pass win. If your ESLint config contains rules you want enforced after formatting, the plugin route cannot express that, because those rules are the ones it disables.

The cost is the extra pass and the extra dependency surface. package.json lists eslint ^10.5.0 and prettier ^3.8.4 as direct dependencies, along with @typescript-eslint/parser, common-tags, indent-string, loglevel-colored-level-prefix, pretty-format, and stable-hash-x. It also declares optional peerDependencies on prettier-plugin-svelte ^4.0.0 and svelte-eslint-parser. That is a heavier install than a config preset.

## Maintenance, licence and what upgrading prettier-eslint costs

The repository is not archived, and the last push was on 2026-09-17. Releases are tagged and versioned: v17.1.2 on 2026-08-20, v17.1.1 on 2026-06-19, and v17.1.0 on 2026-06-15. The project also has a .changeset directory, which indicates release notes are generated per change.

The licence is MIT, stated in the README badge and in package.json. MIT permits use, modification and redistribution with the licence and copyright notice retained; it also disclaims warranty. That is the extent of what the repository states, and it is not legal advice.

The upgrade cost is structural rather than cosmetic. The package declares eslint ^10.5.0 and prettier ^3.8.4 as direct dependencies, not peer dependencies, so upgrading prettier-eslint can move the ESLint and Prettier versions used when no local copy is resolvable from filePath. The README's resolution rule is explicit about this fallback, and eslintPath and prettierPath are the levers if you want to pin the tooling yourself. The engines field also moves: ^20.19.0 || ^22.13.0 || >=24 means an older Node runtime blocks the install outright. For a devDependency that runs in a build script, that is a coordinated upgrade across the Node version, the ESLint version and the Prettier version, not a one-line bump.

## Conclusion

Adopt prettier-eslint if you already have an ESLint config whose rules must survive Prettier's formatting and you want that reconciliation inside one Node call, for example from a script or a custom tool. Do not adopt it if you have moved to flat-config-only tooling and expect a first-party plugin, or if you need ESLint to report every rule violation rather than only parsing failures. Verify first that your Node version satisfies the engines field (^20.19.0 || ^22.13.0 || >=24), that the eslint and prettier you want are resolvable from the filePath you pass, and whether prettierLast matches the direction you want. The README does not document rollback or failure recovery, so a failed run is something you handle around the call.

## FAQ

### What is prettier-eslint and what does it do?

It is a Node module that formats code with prettier and then passes the result to eslint --fix, so you get Prettier's formatting plus ESLint's configuration. For .css, .less, .scss and .json files it runs only prettier, since eslint cannot process those.

### How do I set up prettier-eslint?

Install it as a devDependency with npm install --save-dev prettier-eslint, then call the default export with an options object containing text plus either eslintConfig or filePath. prettierOptions are inferred from the ESLint config when you do not supply them.

### How do I use prettier-eslint in VS Code?

The README and package.json describe a Node module only; no editor extension is documented. The documented integration point is the format and analyze functions called from JavaScript.

### Is there a prettier-eslint alternative?

The related approach is eslint-config-prettier with eslint-plugin-prettier, which disables conflicting ESLint rules and runs Prettier as an ESLint rule instead of running both formatters in sequence. prettier-eslint's difference is that eslint --fix runs after prettier, so ESLint rules can override the formatting.

### How do I use prettier-eslint?

Call the default export with an options object: text holds the source, and you pass either eslintConfig or filePath. The README's example awaits the result and gets back the formatted string.

## Sources

- [License: MIT](https://github.com/prettier/prettier-eslint/blob/master/LICENSE)
- [prettier/prettier-eslint on GitHub](https://github.com/prettier/prettier-eslint)
- [Project website](https://opencollective.com/prettier-eslint)
- [README](https://github.com/prettier/prettier-eslint/blob/master/README.md)
- [Releases](https://github.com/prettier/prettier-eslint/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/prettier-prettier-eslint
