My Blogs

About me

JS Linting & Formatting Setup: ESLint, Prettier, Husky & Lint-staged

LAST UPDATED AT: Sep 8, 2025

ESlint and Prettier are two essential frontend libraries for enhancing developer experience (DX) when working in a team.

In short, Eslint ↗️ is to enforce good coding guidelines or linting,

Prettier ↗️ is for code formatting.

Along with these two libraries we will also be using husky and lint-staged.

Lint-staged is used for running actions on the git staged files, like in this case - linting, formatting

Husky is used for triggering actions based on certain Git hooks. For example: in this case, we want to run invoke lint-staged during git commit.

A typical dev workflow

Note: I will be using React and TypeScript setup in this blog. The same can be applicable to other frontend projects.


𓊍 Steps at a glance

  • Uninstall existing eslint libraries
  • Initialize eslint config
  • Running lint command and enhancing eslint.config.js
  • Install and setup: husky and lint-staged
  • Prettier setup

Note: From Eslint v9 , the flat config system is adopted, which is an array of eslint configs structure. More here ↗️

1) Uninstall existing eslint libraries

Boilerplate setup like Vite come with some eslint setup, so it is good to clean up and to setup from scratch to use the updated version of the libraries.

  • Remove .eslintrc.cjs
  • Uninstall existing eslint libraries (check for other related eslint plugins and configs) and do a clean install
npm uninstall eslint npm i

2) Initialize eslint config

Run this command

npm init @eslint/config@latest

When you run this, you’ll be asked a series of questions to determine how you’re using ESLint and what options should be included. After answering these questions, you’ll have an eslint.config.js (or eslint.config.mjs) file created in your project. A typical dev workflow

3) Running lint command and enhancing eslint.config.js

We will first manually test our eslint setup . In the package.json, add this new script command.

"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",

Now run npm run lint.

If you get a common error in all files related to React in scope:

error 'React' must be in scope when using JSX react/react-in-jsx-scope

To solve this, you have to configure the eslint-plugin-react to identify the JSX.transform from React 17 ↗️

Below is the code diff (left side is the updated code): update eslint-config-react

Note: For Typescript projects, you can add the rule

"react/prop-types": "off"

Additionally, for react-hooks linting we can also install the eslint-plugin-react-hooks, More here ↗️

4) Install and setup: husky and lint-staged

We will now install husky and lint-staged to automatically check for lint errors during git commit (Prettier is coming next!).

npm install --save-dev husky lint-staged npx husky init

This will add a file in .husky/pre-commit . Update the content of the file to npx lint-staged.

Now, to tell lint-staged to run lint during git commit , add the command in package.json (Note: this will be top level of package.json)

"lint-staged": { "*.{ts,tsx}": [ "npm run lint", ] }

This will instruct to run the lint command (of step 3) during git commit on any .tsx, .ts files.

To test, go to any .ts or .tsx file and make some dummy change, then commit.

git add .
git commit -m "Keep calm and commit"

5) Prettier setup

Install two libraries as dev dependencies

npm i --save-dev prettier eslint-config-prettier

eslint-config-prettier is an ESLint configuration designed to disable ESLint rules that might conflict with Prettier's formatting rules. This ensures that ESLint and Prettier can work together without generating conflicting warnings or errors related to code style.

To configure Prettier, create two files in the project root: .prettierignore and .prettierrc

// .prettierignore # Ignore artifacts: build coverage
// .prettierrc {}

We can keep the .prettierrc file an empty object so that it follows the default config of prettier.

Now add the eslint-config-prettier config in eslint.config.ts at the last but before the rules

import eslintConfigPrettier from "eslint-config-prettier/flat";



export default defineConfig([
  ...
  eslintConfigPrettier, 
  rules:[
    ....
  ]
])

Now, add two script commands in package.json

"prettier:check": "npx eslint-config-prettier eslint.config.js",
"format": "npx prettier --write --ignore-unknown"

The first command is a helper command to help you to check if any conflict between eslint and prettier.

The second command (format) is to run prettier on the files.

Now, update the lint-staged command in package.json to:

"lint-staged": {
    "*.{ts,tsx}": [
      "npm run lint",
      "npm run format"
    ]
},

Conclusion

Setting up ESLint, Prettier, Husky, and lint-staged might feel like overhead initially, but it's an investment that pays dividends quickly. This 30-minute setup will save you countless hours of code review discussions about formatting and catch bugs before they reach production.

As your team grows, these tools become even more valuable. New developers automatically follow the same standards, and your codebase maintains a consistent, professional appearance that becomes your team's signature.

chao 👋