Improving Code Quality with Pre-Commits in JavaScript Projects (Husky)
I am a dedicated software engineer specializing in front-end development and clean coding practices, bolstered by a comprehensive full-stack background. My professional journey blends technical expertise with effective team leadership, focusing on fostering an environment of continuous learning and accountability. Driven by a passion for technology and people, I aim to innovate and inspire in every project I undertake.
"Code quality is critical to project success, but catching issues early can be a challenge. Pre-commit hooks provide a powerful way to shift quality checks left, saving time and reducing costly fixes. This blog will guide you through setting up pre-commit hooks for your JavaScript and TypeScript projects."
TTL;DR: Quick Setup Guide
Use this commands if you don’t like to read the details, otherwise go to next step
# BASIC CONFIG
git init # Start git config
npm init # Start npm config
mkdir src # Create a src folder
touch ./src/index.js # Create a file
echo -e "node_modules\n.pnpm-store" > .gitignore # Create a .gitignore file to exclude unnecessary files like node_modules and .pnpm-store from version control.
# HUSKY
pnpm add --save-dev husky
pnpm exec husky init
echo "pnpm pre-commit" > .husky/pre-commit
# LINT STAGED
pnpm add --save-dev lint-staged # requires further setup
echo "{ \"./src/**/*\" :[\"eslint --fix\"], \"*.{json,js,ts,jsx,tsx,html}\": [\"prettier --write --ignore-unknown\"]}" > .lintstagedrc
# ESLINT
pnpm create @eslint/config@latest # Answer the questions according to you current project needs.
pnpm eslint ./src/* # run eslint to detect errors
# PRETIER
pnpm add --save-dev --save-exact prettier eslint-config-prettier # Install prettier
echo "{}" > .prettierrc # Create config file for prettier
echo "build\ncoverage\n" >> .gitignore # Add ignored folers
pnpm exec prettier . --write # Format your JS file
# COMMIT LINT
pnpm add --save-dev @commitlint/{cli,config-conventional} # Install commit lint
echo "export default { extends: ['@commitlint/config-conventional'] };" > commitlint.config.mjs # Create commit lint config file
echo "pnpm dlx commitlint --edit \$1" > .husky/commit-msg # Add commit message linting to commit-msg hook
add this to the package.json
{
"main": "./src/index.js",
"type": "module",
"scripts": {
"pre-commit": "lint-staged",
"prepare": "husky",
}
}
eslint.config.js
import globals from "globals";
import pluginJs from "@eslint/js";
import eslintConfigPrettier from "eslint-config-prettier";
/** @type {import('eslint').Linter.Config[]} */
export default [
{ languageOptions: { globals: globals.browser } },
pluginJs.configs.recommended,
eslintConfigPrettier,
];
Initial project config
Set up a basic JavaScript project:
git init # Start git config
npm init # Start npm config
mkdir src # Create a src folder
touch ./src/index.js # Create a file
echo -e "node_modules\n.pnpm-store" > .gitignore
Update ./src/index.js with the following content:
//This function has an intentional bad structure and error for exercise purposes.
export function sayHello(number
)
{
if(number % 2 = 0){
return true;
}
return false;
}
Make your first commit
git add .
git commit -m "new code change" # this commit dont follow any standar for exercise purpose
git log --oneline # log you current commits
Note that this commit contains errors, which is why tools like Husky and ESLint are essential to prevent such issues from progressing further.
🧰 Basic tools configuration
Key Tools to Use
Husky: Automates Git hooks for running pre-commit checks.
Lint-Staged: Ensures only staged files are linted for efficiency.
ESLint: Catches code quality issues and enforces coding standards.
Prettier: Formats code for consistent indentation and readability.
Commit-Lint: Validates commit messages for clarity and consistency.
For this blog we will use pnpm, so install it pnpm.
1 . Install Husky ( link )
Install and initialize Husky:
pnpm add --save-dev husky
pnpm exec husky init
This creates a .husky folder with a pre-commit file. Update it to run lint checks:
|_.husky
|__ pre-commit
change the ./.hunsky/pre-commit file to execute the lint job
echo "pnpm pre-commit" > .husky/pre-commit
Now, add another commit with the following errors for testing:
// Intentional bad structure for testing
export function sayHello(number
)
{
const result = false;
if(number % 2 == 0){
result = true;
}
return result;
}
Update package.json to include pre-commit linting:
{
"name": "precommit-ts",
"main": "./src/index.js",
"type": "module",
"scripts": {
"pre-commit": "lint-staged",
"prepare": "husky",
},
"devDependencies": {
"@eslint/js": "^9.18.0",
"eslint": "^8.57.1",
"globals": "^15.14.0",
"husky": "^9.1.7"
}
}
2 . Install Lint-staged
Configure lint-staged to lint only staged files:
pnpm add --save-dev lint-staged # requires further setup
echo "{ \"./src/**/*\" :[\"eslint --fix\"], \"*.{json,js,ts,jsx,tsx,html}\": [\"prettier --write --ignore-unknown\"]}" > .lintstagedrc
3 . Install Eslint ( link )
pnpm create @eslint/config@latest # Answer the questions according to you current project needs.
pnpm eslint ./src/* # run eslint to detect errors
4 . Install Prettier & eslint+prettier
This allow you to ident your code
pnpm add --save-dev --save-exact prettier eslint-config-prettier # Install prettier
echo "{}" > .prettierrc # Create config file for prettier
echo "build\ncoverage\n" >> .gitignore # Add ignored folers
pnpm exec prettier . --write # Format your JS file
Add eslint-config-prettier to your ESLint configuration – either to eslintrc or to eslint.config.js (flat config).
import globals from "globals";
import pluginJs from "@eslint/js";
import eslintConfigPrettier from "eslint-config-prettier";
/** @type {import('eslint').Linter.Config[]} */
export default [
{ languageOptions: { globals: globals.browser } },
pluginJs.configs.recommended,
eslintConfigPrettier,
];
5 . Commit lint.
follow official docs for updated instructions
pnpm add --save-dev @commitlint/{cli,config-conventional} # Install commit lint
# Create commit lint config file
echo "export default { extends: ['@commitlint/config-conventional'] };" > commitlint.config.mjs
echo "pnpm dlx commitlint --edit \$1" > .husky/commit-msg # Add commit message linting to commit-msg hook
Conventional Commits enforce a clear, standardized commit message format that organizes Git history, improves traceability, and simplifies collaboration. By categorizing changes (e.g., feat, fix), it enables automated versioning, streamlined release note generation, and easier error identification. This ensures consistency, accelerates code reviews, and supports tools like Semantic Versioning. Learn more at Conventional Commits.
Conclusion
By implementing pre-commit hooks and using tools like Husky, ESLint, Prettier, and Commit-Lint, you ensure cleaner commits, reduce errors, and foster better collaboration.
These steps will help you shift left and maintain high code quality from the start.


