Lint Staged With Husky for Pre-commit Validations
Lint Staged With Husky for Pre-commit Validations

Really often we want to run some check for our code like linting before our commit. And we can easily do it by using lint-staged and husky. This is why in this post you will learn how to use them together in order to implement such functionality.

Do we have a problem?

What do we want to solve? Just imagine that you have a huge project. And you need to run some linting, tests or code formatting in your changed files. You worked on some feature, you finished it and now you want to run this checks.

Here we have 2 problems. If you just run commands from the console, you will run them for all files inside your project. It doesn't make any sense because it will ages. And if your project is super big it can take several minutes.

What we want to do instead is to run all commands just for our changed files.

Lint-staged

Typically you are using Git and it knows exactly what files you changed.

The second important point is that every developer can forget to run these commands by hands before they make the commit or push their changes to remote repository.

Which actually means that we must somehow configure this logic so all these commands will be called automatically and they will be called only for changed files.

For this in Javascript world we use 2 packages: lint-staged and husky.

We use lint-stage to run commands only for our changed files. What are changed files? These are just staged files inside Git.

Git staged

As you can see after git add . we have green staged file. This is exactly what lint-staged will run commands on.

Lint-staged runs linters (on any other commands) on staged Git files.

Husky

The second package that we will use is called husky. This package can add prehooks for commit or push inside Git. Let's say that we want to run Eslint and Prettier before commit. But we want to do it automatically. This is exactly why do we need husky.

What I want to do now is configure lint-staged and husky for our project. I have create-react-app but it doesn't matter. It will work with any project.

Installation

The first step here will be to install both packages.

npm install husky
npm install lint-staged

Our next step here is to install libraries or tools that we will use for linting or prettifying. This is why here I want to configure Prettier and then Eslint because these are 2 most popular linters.

npm install prettier
npm init @eslint/config

We used npm init which will not only install Eslint for us but also create a default config file. Here we get some questions to create correct configuration. I just hit enter on every question.

Lint-staged configuration

All our packages are installed. Now we can jump back in our package.json and configure here our lint-staged.

"lint-staged": {
  "src/**/*.{js,html.css}": "prettier --write",
  "src/**/*.js": "eslint"
}

Here we defined that for all Javascript, Html and CSS files in src directory we want to run prettier and apply formatting to the files. For Javascript files we want also to run eslint command.

Now inside console we can directly run lint-staged command which will use correct config.

npm lint-staged

Lint-staged

As you can see lint-staged didn't do anything because we don't have any changes. But the command is working fine.

Husky configuration

Now we must configure husky package. And actually for husky we have a special command which will install our husky script and configure pre-commit for Git.

npx husky-init && npm install
npx husky add .husky/pre-commit "npm test"

First command will install husky and create Git hooks. Second command will add new pre-commit script with npm test string.

Now inside our project we have new .husky folder. Inside we have a pre-commit file which we just created. Also we have .husky/_/husky.sh. This is a shell script that we should not touch. But what we want to do now is update pre-commit file.

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged

Instead of npm test we must call lint-staged just like we did in console. This means that before every commit we will call our lint-staged commands for all changed files like we defined.

Now let's make some change and do a commit.

Finished project

As you can see at the moment of our commit lint-stage command was called and Eslint failed with an error. Which actually means that we successfully configured lint-staged together with husky to make pre-commit checks for our changed files.

And actually if you are interested in learning Git for everyday work make sure to check my Git course which has everything that you need for good Git skills.

📚 Source code of what we've done