Articles

ESLint vs Prettier: Linting vs Formatting

ESLint catches bugs and enforces code patterns; Prettier only reformats how code looks. Why most JavaScript projects run both, not one or the other.

Takina Takina · · 4 min read
Dark-themed code editor showing JavaScript

ESLint and Prettier are both tools that run against your JavaScript or TypeScript code before it ships, but they solve different problems and most projects run both rather than choosing between them. ESLint is a linter — it analyzes your code for bugs, suspicious patterns, and style-rule violations, and can flag or fail on things like unused variables or unreachable code. Prettier is a formatter — it only rewrites whitespace, line breaks, quote style, and similar layout details, with no opinion at all about whether the code is correct.

What ESLint actually catches

ESLint parses your code into an abstract syntax tree and runs a configurable set of rules against it. Some of those rules catch real bugs:

  • Referencing a variable before it’s declared
  • Using == where === was clearly intended
  • An unreachable return after another return
  • A Promise created but never awaited or handled
  • Unused imports or variables left over from a refactor

Other rules are stylistic — enforcing consistent naming conventions, restricting certain patterns your team has decided against, or requiring a specific quoting style. ESLint’s rule set is what makes it valuable beyond formatting: it understands the meaning of the code well enough to flag things that are syntactically valid but semantically wrong.

Because ESLint rules can be arbitrarily complex, teams often layer on shared configs (like framework- or org-specific presets) rather than hand-picking every rule. Custom rules can also enforce project-specific conventions — banning a deprecated internal API, for instance — that no generic tool would know to check for.

What Prettier actually does

Prettier reformats code to a single, consistent style — indentation, line width, quote character, trailing commas, where to break long lines — and, critically, has almost no configuration surface by design. That’s a deliberate philosophy: instead of a team debating tabs vs spaces or single vs double quotes rule by rule, Prettier picks sensible defaults and reformats everything to match, ending the debate entirely rather than encoding it as a set of lintable rules.

Prettier doesn’t understand what your code does — it only understands its grammar well enough to reprint it consistently. It will happily reformat code with an infinite loop or an unused variable, because catching bugs was never its job.

Why teams run both

ESLintPrettier
PurposeCatch bugs and enforce code patternsEnforce consistent formatting
Understands code meaningYesNo — grammar only, not semantics
ConfigurableHighly — hundreds of rules, custom rules possibleMinimal by design
Can auto-fixSome rules, not allYes, effectively all formatting
Typical failure mode if skippedReal bugs and inconsistent patterns shipInconsistent whitespace and style in diffs

Running both means ESLint focuses purely on correctness and pattern rules, while formatting is handled entirely by Prettier — which is why most modern ESLint setups explicitly disable ESLint’s own formatting-related rules (via a config like eslint-config-prettier) rather than letting the two tools disagree about how a line should be broken. Without that, you can end up with ESLint and Prettier fighting over the same line, each “fixing” it back to what the other wants.

Where they fit in the workflow

Both tools are typically wired into a few places:

  • Editor integration, so violations show up as you type rather than at commit time.
  • A pre-commit hook, often via a tool that only checks staged files, catching issues before they enter version control.
  • CI, running both as a required check so a pull request can’t merge with lint errors or unformatted code, alongside whatever CI/CD pipeline already runs your tests.

Formatting arguments in code review are close to a solved problem once Prettier is wired in and auto-formatting on save — nobody needs to comment “add a semicolon here” when the formatter handles it automatically on every save or commit. That leaves review comments for things that actually matter: logic, naming, and architecture, not whitespace.

Setup basics

A typical project installs both as dev dependencies, configures ESLint with a base config plus any project-specific rules, adds a minimal Prettier config (often just the defaults, or a couple of overrides like print width), and disables ESLint’s formatting rules so the two don’t conflict. Package managers like those compared in npm vs pnpm vs yarn all handle installing and running both the same way, via scripts in package.json or directly through each tool’s CLI.

The takeaway

ESLint and Prettier aren’t competitors — ESLint catches bugs and enforces code patterns by understanding what your code means, while Prettier enforces consistent formatting without caring what the code does. Running both, with ESLint’s own formatting rules turned off in favor of Prettier, gives you a linter focused purely on correctness and a formatter that ends style debates outright, instead of one tool trying to do both jobs at once.

Takina Takina · · 4 min read

What Is a Lockfile? Reproducible Dependency Installs

A lockfile records the exact dependency versions your package manager resolved, so every install — from your laptop to CI — reproduces the same tree.

#JavaScript #Web Development #Developer Tools
Takina Takina · · 4 min read

JavaScript Intl API: Formatting Dates and Numbers

The Intl API formats dates, numbers, and currency using a user's locale without a library. How Intl.DateTimeFormat and Intl.NumberFormat work.

#JavaScript #Web Development #Developer Tools
Takina Takina · · 5 min read

Turbopack vs Webpack: Choosing a JS Bundler

Turbopack is a Rust-based bundler built for incremental speed; Webpack is the mature, plugin-heavy standard. How they differ and when to pick each.

#JavaScript #Web Development #Developer Tools