Config Formats

Prettier and ESLint Together: Formatting and Linting That Do Not Fight

Prettier handles formatting and ESLint enforces quality, but when their rules overlap they fight each other and pre-commit runs get messy. This guide explains how to divide responsibilities, turn off conflicting rules, unify config, and enforce everything in CI.

By LaoHand Team·7 min read·Updated 2026-09-06

Get the Division Clear: Prettier Formats, ESLint Judges

The two responsibilities are completely different: Prettier only cares how code looks — quotes, semicolons, indentation, line wrapping — it is the formatter; ESLint cares whether code violates rule errors — unused variables, potential bugs, enforced style — it is the language judge.

Teams conflate them, and end up configuring the same indentation rule in both, which fights. The correct mental model: hand all formatting concerns to Prettier, and disable every ESLint rule about formatting (indent, quotes, semi), letting Prettier own them. Each side keeps its lane and rules stop clashing.

{"semi": true, "singleQuote": false, "trailingComma": "all"}
# ----------------------------------------------
// .prettierrc 常见配置
module.exports = {
  semi: true,
  singleQuote: true,
  printWidth: 100
};

Turn Off Conflicting Rules with eslint-config-prettier

ESLint ships many formatting rules that conflict with Prettier (indent, quotes, no-mixed-spaces-and-tabs, etc.). The easiest fix is eslint-config-prettier, which turns off every formatting-related rule in one shot.

Concretely, put prettier as the last entry in your ESLint config’s extends array. Order matters: later entries override, so prettier must be last for its "disable" to actually beat the rule sets declared before it.

After configuring, run npx eslint . and confirm no leftover formatting errors. If formatting or style complaints remain, an inline disable comment or an un-turned-off rule is usually the cause — re-check the extends ordering.

// .eslintrc.js
module.exports = {
  extends: [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "prettier"   // 必须放最后,关闭与格式冲突的规则
  ]
};

Unify Once: Get Prettier into Both Editor and CI, Not Just Locally

Installing the dependency is not enough; formatting must apply in the editor live. In VSCode, install the Prettier extension, set default formatter to prettier, and enable format on save alongside eslint --fix on save. Every line you commit then comes pre-formatted.

But the only reliable way to stop "I formatted locally, they committed unformatted" drift is a hard CI gate: run prettier --check . and eslint . and block the merge on either failure. Locally, use a pre-commit hook (lint-staged) for incremental formatting of staged files plus a plain CLI run as backup — a double safety net.

# VSCode settings.json
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }
# -------------------------------------------------
# package.json scripts
"lint": "eslint .",
"format:check": "prettier --check ."

lint-staged: Pre-commit on Staged Files Only, Not the Whole Tree

Running eslint . directly in a husky pre-commit is slow on big repos, because every commit scans the whole tree. lint-staged lets you format and lint only the currently staged files, driven by the staged list of .ts/.tsx/.vue files.

The classic husky + lint-staged combo: on commit, run prettier --write then eslint --fix on staged files, and only allow the commit to proceed if both pass. Be careful to list every extension you want handled (vue, ts, tsx, json, md), because an unlisted type is effectively unchecked.

This way every commit is already formatted at the moment you make it, and CI’s check uses --check to verify rather than rewrite, keeping local and CI results consistent.

// .lintstagedrc
{
  "*.{js,ts,tsx,vue}": ["prettier --write", "eslint --fix"],
  "*.{json,md,yaml}": ["prettier --write"]
}
# -------------------------------------------------
// package.json
"lint-staged": {
  "*.{js,ts,tsx,vue}": ["prettier --write", "eslint --fix"]
}

Verify: Establish a Baseline and Compare Before/After Formatting

Closing out config cannot rely on "it runs". First, craft a deliberately messy sample file (mixed quotes, inconsistent indentation, overlong lines) and run prettier --write to watch it normalize to the intended uniform style; then run eslint . to confirm formatting rules no longer conflict and only real quality issues remain.

Then prettier --check . should return 0 and eslint . should have zero errors. Commit once to confirm the lint-staged hook actually blocks (deliberately stage some sloppy formatting and watch it get rejected). Finally put the same checks in CI and note in the team README "any style issue, run the formatter first, do not hand-fix".

Make .prettierrc and the ESLint config a required review item so nobody sneaks in a conflicting rule for a one-off need later.

npx prettier --write ./demo.ts   # 先统一坏文件
npx prettier --check .            # 期望退出码 0
npx eslint .                      # 期望 0 error
echo $LASTEXITCODE                # 0 表示通过

Official References

Each command links to its official documentation below, so you can verify the latest usage and read deeper.