JavaScript Linting & Formatting with ESLint and Prettier

This article explains how linting and formatting work in JavaScript, how ESLint and Prettier fit together, and how to set them up so your code stays readable and consistent.

Quick answer: ESLint finds code quality and correctness problems, while Prettier rewrites code into a consistent style. In most JavaScript projects, you use both: ESLint to catch mistakes and Prettier to handle formatting.

Difficulty: Beginner

Helpful to know first: You'll understand this better if you know basic JavaScript syntax, how package managers install tools, and how config files work.

1. What Does Linting and Formatting Do?

Linting and formatting solve two different problems in a JavaScript codebase. Linting checks your code for possible bugs, suspicious patterns, and rule violations. Formatting changes the visual layout of the code so it follows a consistent style.

Linting and formatting are related, but they are not the same thing. A file can be perfectly formatted and still contain a bug, and a file can be valid JavaScript but still be hard to read.

2. Why ESLint and Prettier Matter

As a JavaScript project grows, style differences and small mistakes become expensive. One developer uses single quotes, another uses double quotes, and a third writes long lines that are hard to scan. At the same time, simple mistakes like unused variables, unreachable code, or missing dependencies can slip into reviews.

Using ESLint and Prettier helps teams by making code quality rules repeatable and automatic. That means less manual review time, fewer style debates, and faster onboarding for new developers.

They are especially useful when:

3. Basic Setup and Core Idea

ESLint and Prettier are typically installed as project dependencies. ESLint reads a configuration file and reports violations. Prettier reads a configuration file and formats files according to its rules.

Minimal ESLint example

The following example shows a simple ESLint configuration for modern JavaScript.

export default [// eslint.config.js
  {
    files: ["**/*.js"],
    rules: {
      "no-unused-vars": "error",
      "no-undef": "error"
    }
  }
];

This config says: check JavaScript files, and report unused variables and undefined names as errors.

Minimal Prettier example

Prettier usually needs only a small config, or no config at all if you are fine with its defaults.

// prettier.config.js
export default {
  semi: true,
  singleQuote: true,
  trailingComma: "all"
};

This config controls style only. It does not decide whether a variable is unused or whether a function call is valid.

4. Step-by-Step Examples

Example 1: Catching an unused variable

One of the most common ESLint checks is finding variables that are declared but never used.

const name = "Ada";
const message = "Hello";

console.log(name);

If message is never used, ESLint can report it so you can remove dead code before it accumulates.

Example 2: Auto-formatting inconsistent spacing

Prettier can rewrite uneven spacing and line breaks into a consistent layout.

const items = [1,2,3];
function sum(values){return values.reduce((total,value)=>total + value,0);}

After formatting, that code becomes easier to read and review, even though the logic does not change.

Example 3: Fixing a broken reference

ESLint can also detect a name that was never declared.

function greet() {
  return "Hello, " + username;
}

This often triggers a no-undef error because username does not exist in scope. That is the kind of mistake formatting tools cannot find.

Example 4: Running both tools in a typical file

Here is a small JavaScript module that benefits from both tools working together.

const taxRate = 0.2

function calculateTotal(price, quantity) {
  return price * quantity * (1 + taxRate)
}

console.log(calculateTotal(10, 2))

Prettier can add semicolons, spacing, and line breaks, while ESLint can enforce rules such as no unused values or accidental globals.

5. Practical Use Cases

6. Common Mistakes

Mistake 1: Using Prettier to catch logic bugs

Prettier is only a formatter. It cannot tell you whether a variable is undefined, whether a function is unreachable, or whether you imported the wrong name.

Problem: This assumption leaves real code issues undetected because formatting success does not mean the code is correct.

const total = price * count;
console.log(total);

Fix: Use ESLint for correctness checks and Prettier for formatting.

const price = 10;
const count = 2;
const total = price * count;
console.log(total);

The corrected version works because linting catches missing names while formatting handles the final style.

Mistake 2: Letting ESLint and Prettier fight over style

Some ESLint rules overlap with Prettier, especially around quotes, semicolons, and line width. If both tools try to control the same style decisions, you can get repeated warnings or endless auto-fix changes.

Problem: Conflicting rules can cause save-on-format loops, noisy lint errors, and inconsistent autofix results.

const title = "Docs";

Fix: Let Prettier own formatting and disable conflicting ESLint style rules through a Prettier integration setup.

// Example idea: keep lint rules for code quality, not visual style.
const title = "Docs";

This approach works because each tool stays focused on its own job.

Mistake 3: Forgetting to run linting in CI or before commits

If you only rely on your editor, code can still reach the repository with lint errors when someone skips the editor integration or copies files from elsewhere.

Problem: Without a project-level check, a bad change can pass local editing and fail later in review or production.

function formatPrice(value) {
  return "$" + value;
}

Fix: Run ESLint and Prettier through scripts that every contributor can execute.

// package.json scripts
"scripts": {
  "lint": "eslint .",
  "format": "prettier --write ."
}

The corrected workflow works because checks happen consistently, not just inside one editor.

7. Best Practices

Practice 1: Use Prettier for style, not judgment

Prettier is best when you let it decide formatting automatically. That keeps human reviewers focused on logic instead of commas and indentation.

const profile = {
  name: "Mina",
  role: "Developer"
};

When formatting is automated, the team does not need to discuss visual style in every pull request.

Practice 2: Keep ESLint rules focused on correctness

ESLint is most valuable when it enforces rules that prevent bugs or improve code quality, such as unused variables, invalid references, or unsafe patterns.

function buildLabel(name) {
  if (!name) {
    return "Unknown";
  }

  return name.trim();
}

This kind of rule set helps find problems that a formatter would never notice.

Practice 3: Automate on save and in CI

Fast feedback matters. If your editor formats on save and your CI job runs linting on every push, errors are caught early and cheaply.

# Example CI commands
npm run lint
npm run format:check

Automation reduces the chance that style drift or lint errors survive until late review.

8. Limitations and Edge Cases

Tip: If formatting seems to do nothing, check whether the editor is using the right config file and whether the file type is actually included in the formatter settings.

9. Practical Mini Project

Here is a small project setup that combines linting and formatting for a simple JavaScript utility file.

// package.json
{
  "type": "module",
  "scripts": {
    "lint": "eslint .",
    "format": "prettier --write .",
    "format:check": "prettier --check ."
  }
}
// eslint.config.js
export default [
  {
    files: ["**/*.js"],
    languageOptions: {
      ecmaVersion: 2020
    },
    rules: {
      "no-unused-vars": "error",
      "no-undef": "error"
    }
  }
];
// math.js
const rate = 0.15;

export function finalPrice(basePrice) {
  return basePrice + basePrice * rate;
}

This setup gives you a repeatable workflow: lint for correctness, format for consistency, and run both from scripts or editor commands.

10. Key Points

11. Practice Exercise

Expected output: the file becomes consistently formatted, while ESLint still reports the unused or undefined names until you fix them.

Hint: If the formatter changes only whitespace and punctuation, that is expected. If you want bug detection, you need linting rules.

// example.js
const total = 10
const temp = missingValue

console.log(total)

12. Final Summary

ESLint and Prettier are the standard JavaScript tools for keeping code clean, readable, and reliable. ESLint checks for mistakes and risky patterns, while Prettier handles the visual shape of your code. Used together, they create a workflow that is easier to maintain than manual style reviews.

The main idea is simple: do not ask one tool to solve both problems. Let Prettier format, let ESLint lint, and make both part of your editor and project scripts. If you do that, you will spend less time arguing about style and more time improving the actual code.

Next, you can learn how to wire these tools into your editor or how to set up pre-commit checks so formatting and linting happen automatically before code is shared.