Sie speichern eine Datei, Prettier formatiert sie neu, und ESLint meldet sofort einen Fehler in der Zeile, die Prettier gerade angefasst hat. Beide Tools sind konfiguriert, beide laufen, und statt zu helfen, bekämpfen sie sich gegenseitig.

Die beiden Tools erledigen unterschiedliche Aufgaben. Das Problem: Beide haben eine Meinung zu genau einer Sache — wie der Code auf der Seite aussieht.

Was ESLint tatsächlich prüft

ESLint zerlegt Ihren Code in einen abstrakten Syntaxbaum und führt Regeln gegen dessen Struktur aus. Der größte Teil seines Nutzens kommt von Regeln, die echte Bugs finden: eine Variable, die nie definiert wird, ein nicht erreichbares return nach einem anderen return, ein ungenutzter Import, der nach einem Refactoring übrig blieb, ein doppelter Objekt-Key, der den ersten lautlos überschreibt. Das sind semantische Prüfungen — ESLint liest, was der Code tut.

ESLint liefert auch stilistische Regeln, etwa die Pflicht zu einfachen Anführungszeichen oder Semikolons und ein Limit für die Zeilenlänge — auch wenn diese seit Version 8.53 zugunsten des separaten Projekts ESLint Stylistic als veraltet markiert sind, und sie finden ohnehin keine Bugs. Sie fangen Stilfragen ab, und genau das ist der Teil, der mit Prettier kollidiert.

Was Prettier tatsächlich macht

Prettier interessiert sich nicht für die Bedeutung Ihres Codes. Es parst ihn, verwirft die ursprüngliche Formatierung und druckt die gesamte Datei nach eigenen Regeln für Einrückung, Zeilenlänge, Anführungszeichen-Stil und Umbruchstellen bei langen Zeilen neu. Führen Sie es zweimal auf derselben Eingabe aus und Sie erhalten byte-identische Ausgabe — genau das ist der Zweck. Es gibt fast nichts zu konfigurieren, bewusst so angelegt: eine .prettierrc, eine Handvoll Optionen, und jede Datei im Projekt sieht gleich aus, unabhängig davon, wer sie geschrieben hat.

Führen Sie prettier --check . in der CI aus, und es sagt Ihnen, ob jede Datei dieser kanonischen Formatierung entspricht. Es weiß nicht und interessiert sich nicht dafür, ob der Code funktioniert.

Warum ESLint und Prettier in Konflikt geraten

Die Überlappung ist schmal. Die stilistischen Regeln von ESLint (Anführungszeichen, Semikolons, Einrückung, abschließende Kommas) decken denselben Bereich ab wie Prettier, und beide stimmen nicht immer überein. Wenn sie es nicht tun, entsteht die Schleife aus dem ersten Absatz: Prettier formatiert eine Zeile neu, ESLint meldet das Ergebnis, Sie „korrigieren" von Hand, Prettier setzt es zurück.

Verlangen Sie also nicht mehr von ESLint, Formatierungsentscheidungen zu treffen. Lassen Sie es nur das tun, was Prettier nicht kann: Bugs und schlechte Patterns finden. eslint-config-prettier ist eine Konfiguration, die jede ESLint-Regel deaktiviert, die mit Prettiers Ausgabe kollidieren oder sie duplizieren könnte. Sie fügt nichts hinzu. Sie deaktiviert nur.

Ein zweites Paket, eslint-plugin-prettier, geht den umgekehrten Weg: Es führt Prettier als ESLint-Regel aus, sodass jeder Formatierungsunterschied als eigener ESLint-Fehler auftaucht. Prettiers eigene Dokumentation bezeichnet dieses Setup als „generell nicht empfohlen". Es ist langsamer, weil ESLint Prettier pro Datei erneut aufruft. Es erzeugt auch mehr Rauschen: Eine einzelne Neuformatierung kann sich in Dutzende ESLint-Fehler statt in einen Diff verwandeln. Führen Sie Prettier stattdessen eigenständig aus.

ESLint und Prettier mit Flat Config einrichten

Seit ESLint 9 ist die Konfigurationsdatei eslint.config.js, ein flaches Array von Konfigurationsobjekten, das das ältere .eslintrc-Format ersetzt. Wenn Sie ein Projekt von null starten:

1
npm init @eslint/config@latest

Das führt Sie durch ein paar Fragen und schreibt eine eslint.config.js. Fügen Sie dann Prettier und die Konfiguration hinzu, die die widersprüchlichen Regeln verstummen lässt:

1
npm install --save-dev --save-exact prettier eslint-config-prettier

eslint-config-prettier liefert einen Flat-Config-Einstiegspunkt. Importieren Sie ihn und setzen Sie ihn als letztes in das Array. Spätere Einträge überschreiben frühere, also kann alles, was danach kommt, eine ausgeschaltete Regel wieder einschalten:

1
2
3
4
5
6
7
8
// eslint.config.js
import js from "@eslint/js";
import eslintConfigPrettier from "eslint-config-prettier/flat";

export default [
  js.configs.recommended,
  eslintConfigPrettier,
];

Fügen Sie eine .prettierrc für Prettiers eigene Optionen hinzu (die meisten Teams brauchen davon fast keine):

1
2
3
4
{
  "semi": true,
  "singleQuote": true
}

Und zwei Scripts in package.json, damit beide Tools unabhängig voneinander laufen:

1
2
3
4
5
6
{
  "scripts": {
    "lint": "eslint .",
    "format": "prettier --write ."
  }
}

eslint-config-prettier liefert außerdem eine CLI-Prüfung, die jede Regel in Ihrer Konfiguration auflistet, die noch mit Prettier kollidiert:

1
npx eslint-config-prettier eslint.config.js

Exit-Code 0 bedeutet keine Konflikte. 2 bedeutet, es hat welche gefunden, und die Ausgabe nennt jede Regel, damit Sie sie entfernen können.

eslint-config-prettier mit typescript-eslint verwenden

Für TypeScript klinkt sich typescript-eslint in dasselbe Flat-Config-Array ein und fügt eigene semantische Regeln über dem einfachen ESLint hinzu — tseslint.configs.recommended verbietet any, das direkt in Ihrem Code geschrieben wird (no-explicit-any). Komplexer ist es, ein any zu erfassen, das aus einem ungetypten Bibliotheksaufruf eingeschleust wurde: Wechseln Sie dafür zu recommendedTypeChecked, das parserOptions.project benötigt, das auf Ihre tsconfig.json zeigt, damit die Regeln den TypeScript-Compiler nach echten Typen fragen können. Die Reihenfolge-Regel ändert sich nicht: eslint-config-prettier kommt immer zuletzt.

1
2
3
4
5
6
7
8
9
import js from "@eslint/js";
import tseslint from "typescript-eslint";
import eslintConfigPrettier from "eslint-config-prettier/flat";

export default [
  js.configs.recommended,
  ...tseslint.configs.recommended,
  eslintConfigPrettier,
];

Keines der beiden Tools prüft, ob Ihre Typen gut entworfen sind. Ein Generic, das mit extends eingeschränkt werden sollte, kommt durch beide Kontrollen — das ist Aufgabe des Compilers. Mehr dazu in TypeScript-Generics Erklärt mit Beispielen.

ESLint und Prettier im Editor und in der CI ausführen

Im Editor aktivieren Sie „format on save" für Prettier und lassen ESLint separat melden, beim Speichern oder in einem Pre-Commit-Hook. Sobald die obigen Konfigurationsdateien existieren, arbeiten die Editor-Erweiterungen für beide Tools unabhängig voneinander. Sie müssen das eine nicht über das andere laufen lassen.

In der CI führen Sie beide als getrennte Schritte aus:

1
2
npx eslint .
npx prettier --check .

Ein fehlgeschlagenes prettier --check bedeutet, jemand hat den Formatter nicht ausgeführt. Ein fehlgeschlagenes eslint bedeutet, der Code hat ein echtes Problem. Zwei Schritte halten diesen Unterschied im CI-Log lesbar.

Häufige Fehler bei der ESLint- und Prettier-Einrichtung

Der häufigste ist, eslint-plugin-prettier zu installieren, weil ein altes Tutorial das so vorschlägt, und sich dann zu fragen, warum eslint . langsam ist und die Problemliste dreimal so lang wie erwartet. Entfernen Sie das Plugin, behalten Sie eslint-config-prettier, führen Sie Prettier eigenständig aus.

Der zweite ist die Reihenfolge. Wenn eine geteilte Konfiguration oder ein projektspezifischer Override nach eslintConfigPrettier im Array steht, schaltet er lautlos eine ausgeschaltete Regel wieder ein. Die CLI-Prüfung von oben fängt das ab, bevor es in eine PR gelangt.

Der dritte ist der Umfang. Dieses Paar ist für JavaScript und TypeScript gebaut. Wenn Sie ein neues Projekt starten und lieber ein einziges, schnelleres Tool für beide Aufgaben hätten, schauen Sie sich Biome an. Das ist ein anderer Kompromiss, kein direkter Ersatz für das Setup oben.

Was Sie heute in Ihrem Projekt tun sollten

Öffnen Sie Ihre package.json. Wenn eslint-plugin-prettier dort steht, deinstallieren Sie es. Fügen Sie eslint-config-prettier als letzten Eintrag in eslint.config.js hinzu, führen Sie npx eslint-config-prettier eslint.config.js aus und beheben Sie, was dort genannt wird. Danach betrifft jeder ESLint-Fehler, den Sie sehen, etwas, das ESLint in der Struktur des Codes gefunden hat — nicht eine Zeile, die Prettier ohnehin neu formatiert hätte. Es findet immer noch nicht alles: ein vergessenes await in einer Schleife oder eine unbehandelte Promise-Rejection ist ein Logikfehler, kein Lint-Fehler, sofern Sie kein Plugin dafür hinzufügen. Wie solche Fehler aussehen, zeigt Async/Await und die Event Loop in JavaScript Erklärt.

Verwandte Artikel