React and typescript components lib, part 4: pre-commit with Husky and lint-staged
Introduction Portuguese version: Biblioteca de componentes React e typescript, parte 4: pré-commit com Husky e lint-staged In part four, pre-commit actions will be added to the application using two libraries: Husky and lint-staged. The goal is to demonstrate the practical implementation of these tools in this article. As a reference, here is the article Husky and lint-staged for pre-commit in React, which was the first one I wrote on dev.to. Libs Husky: is the library that allows pre-commit actions to be executed lint-staged: is the library that defines which pre-commit actions will be executed and applied specifically to the files staged in git — that is, the files that have been added using git add Pre-commit action Pre-commit actions are executed after running git commit in the terminal, but right before the commit is actually finalized. For the application, three pre-commit actions will be defined: Format the files staged in git according to the linting rules, right before the commit is finalized Format the files staged in git according to the prettier rules, right before the commit is finalized Run the tests related to the modified components that have been staged, to validate that they are passing, right before the commit is finalized Setup Husky Adding Husky: yarn add husky --dev At the time of writing this article, the following version was generated: "husky": "^9.1.7" To configure it, run the following command in the terminal: npx husky init This will generate a .husky folder at the root of the project. Inside it, there will be a _ folder containing all available git hooks, although they are not yet configured to run in the project. Outside of the _ folder, there will be a pre-commit file with an initial action already defined. However, since it doesn’t match the application's needs, this file will be modified. In addition to generating the folder mentioned above, a command will also be added to package.json to install Husky inside the app: "scripts": { //... "prepare": "husky" }, Setup lint-staged Adding lint-staged: yarn add lint-staged --dev At the time of writing this article, the following version was generated: "lint-staged": "^15.5.0" To configure Husky to run pre-commit actions together with lint-staged, it is necessary to modify the file inside the .husky folder: pre-commit npx --no-install lint-staged Pre-commit actions Currently, the app has three commands related to the actions to be added to the pre-commit: package.json //... "scripts": { //... "lint-src-fix": "eslint src --fix", "format-src-fix": "prettier src --write", "test": "jest" } By setting up these actions to run, the pre-commit will work successfully: the files staged in git will be updated right before the commit is finalized. However, the downside is that it will run the command on all files in src, even though the pre-commit is supposed to impact only the files that are staged in git. To make the pre-commit actions more efficient, two new scripts will be created: package.json //... "scripts": { //... "lint-fix": "eslint --fix", "format-fix": "prettier --write" } If you try to run these commands directly in the terminal, they won't work successfully because they require a file or folder path to be passed, like: yarn lint-fix {path to folder or file} yarn format-fix {path to folder or file} However, when these commands are run with lint-staged, it detects the files that are staged in git and successfully applies the commands to them. To define the pre-commit actions to be executed through Husky with lint-staged, you need to add the following to package.json: "lint-staged": { "yarn lint-fix", "yarn format-fix", "yarn test --findRelatedTests --bail" }, For the tests, two additional options are passed: --findRelatedTests: runs the tests for the files that are staged in git as well as the ones related to them. By "related," I mean that if you modify and git add a component file like Component.tsx without touching its corresponding test file Component.test.tsx, this command will still know which test is associated with the component and validate it. --bail: stops running tests immediately after the first failure, aborting the commit process Now, the pre-commit actions are more efficient, acting only on the files staged in git. But we can make it even more specific by targeting file extensions for each action. For code formatting and linting, the goal is to apply the rules only to typeScript files .ts and .tsx inside the components folder. For running tests, we will target only .tsx files. Thus, for lint-staged to not only consider the files staged in git but also apply actions based on the file types we want, the configuration will look like this: "lint-staged": { "src/components/**/*.{ts,tsx}": [ "yarn lint-fix", "yarn

Introduction
Portuguese version: Biblioteca de componentes React e typescript, parte 4: pré-commit com Husky e lint-staged
In part four, pre-commit actions will be added to the application using two libraries: Husky and lint-staged. The goal is to demonstrate the practical implementation of these tools in this article. As a reference, here is the article Husky and lint-staged for pre-commit in React, which was the first one I wrote on dev.to.
Libs
- Husky: is the library that allows pre-commit actions to be executed
- lint-staged: is the library that defines which pre-commit actions will be executed and applied specifically to the files staged in git — that is, the files that have been added using
git add
Pre-commit action
Pre-commit actions are executed after running git commit
in the terminal, but right before the commit is actually finalized.
For the application, three pre-commit actions will be defined:
- Format the files staged in git according to the linting rules, right before the commit is finalized
- Format the files staged in git according to the prettier rules, right before the commit is finalized
- Run the tests related to the modified components that have been staged, to validate that they are passing, right before the commit is finalized
Setup Husky
Adding Husky:
yarn add husky --dev
At the time of writing this article, the following version was generated:
"husky": "^9.1.7"
To configure it, run the following command in the terminal:
npx husky init
This will generate a .husky
folder at the root of the project. Inside it, there will be a _
folder containing all available git hooks, although they are not yet configured to run in the project. Outside of the _
folder, there will be a pre-commit
file with an initial action already defined. However, since it doesn’t match the application's needs, this file will be modified.
In addition to generating the folder mentioned above, a command will also be added to package.json to install Husky inside the app:
"scripts": {
//...
"prepare": "husky"
},
Setup lint-staged
Adding lint-staged:
yarn add lint-staged --dev
At the time of writing this article, the following version was generated:
"lint-staged": "^15.5.0"
To configure Husky to run pre-commit actions together with lint-staged, it is necessary to modify the file inside the .husky
folder:
- pre-commit
npx --no-install lint-staged
Pre-commit actions
Currently, the app has three commands related to the actions to be added to the pre-commit:
- package.json
//...
"scripts": {
//...
"lint-src-fix": "eslint src --fix",
"format-src-fix": "prettier src --write",
"test": "jest"
}
By setting up these actions to run, the pre-commit will work successfully: the files staged in git will be updated right before the commit is finalized. However, the downside is that it will run the command on all files in src
, even though the pre-commit is supposed to impact only the files that are staged in git.
To make the pre-commit actions more efficient, two new scripts will be created:
- package.json
//...
"scripts": {
//...
"lint-fix": "eslint --fix",
"format-fix": "prettier --write"
}
If you try to run these commands directly in the terminal, they won't work successfully because they require a file or folder path to be passed, like:
yarn lint-fix {path to folder or file}
yarn format-fix {path to folder or file}
However, when these commands are run with lint-staged, it detects the files that are staged in git and successfully applies the commands to them.
To define the pre-commit actions to be executed through Husky with lint-staged, you need to add the following to package.json:
"lint-staged": {
"yarn lint-fix",
"yarn format-fix",
"yarn test --findRelatedTests --bail"
},
For the tests, two additional options are passed:
- --findRelatedTests: runs the tests for the files that are staged in git as well as the ones related to them. By "related," I mean that if you modify and
git add
a component file likeComponent.tsx
without touching its corresponding test fileComponent.test.tsx
, this command will still know which test is associated with the component and validate it. - --bail: stops running tests immediately after the first failure, aborting the commit process
Now, the pre-commit actions are more efficient, acting only on the files staged in git. But we can make it even more specific by targeting file extensions for each action.
For code formatting and linting, the goal is to apply the rules only to typeScript files .ts
and .tsx
inside the components
folder. For running tests, we will target only .tsx
files.
Thus, for lint-staged to not only consider the files staged in git but also apply actions based on the file types we want, the configuration will look like this:
"lint-staged": {
"src/components/**/*.{ts,tsx}": [
"yarn lint-fix",
"yarn format-fix"
],
"src/components/**/*.tsx": "yarn test --findRelatedTests --bail"
},
package.json
Currently, the package.json
looks like this:
{
"name": "react-example-lib",
"version": "0.3.0",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/griseduardo/react-example-lib.git"
},
"scripts": {
"build": "rollup -c --bundleConfigAsCjs",
"lint-src": "eslint src",
"lint-src-fix": "eslint src --fix",
"lint-fix": "eslint --fix",
"format-src": "prettier src --check",
"format-src-fix": "prettier src --write",
"format-fix": "prettier --write",
"test": "jest",
"prepare": "husky"
},
"lint-staged": {
"src/components/**/*.{ts,tsx}": [
"yarn lint-fix",
"yarn format-fix"
],
"src/components/**/*.tsx": "yarn test --findRelatedTests --bail"
},
"devDependencies": {
"@babel/core": "^7.26.10",
"@babel/preset-env": "^7.26.9",
"@babel/preset-react": "^7.26.3",
"@babel/preset-typescript": "^7.26.0",
"@eslint/js": "^9.19.0",
"@rollup/plugin-commonjs": "^28.0.2",
"@rollup/plugin-node-resolve": "^16.0.0",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "11.1.6",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.2.0",
"@types/jest": "^29.5.14",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"eslint": "^9.19.0",
"eslint-plugin-react": "^7.37.4",
"husky": "^9.1.7",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jest-styled-components": "^7.2.0",
"lint-staged": "^15.5.0",
"prettier": "^3.4.2",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"rollup": "^4.30.1",
"rollup-plugin-dts": "^6.1.1",
"rollup-plugin-peer-deps-external": "^2.2.4",
"styled-components": "^6.1.14",
"typescript": "^5.7.3",
"typescript-eslint": "^8.23.0"
},
"peerDependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0",
"styled-components": "^6.1.14"
}
}
The version will be updated to 0.4.0, since a new version of the library will be released:
{
"name": "react-example-lib",
"version": "0.4.0",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/griseduardo/react-example-lib.git"
},
"scripts": {
"build": "rollup -c --bundleConfigAsCjs",
"lint-src": "eslint src",
"lint-src-fix": "eslint src --fix",
"lint-fix": "eslint --fix",
"format-src": "prettier src --check",
"format-src-fix": "prettier src --write",
"format-fix": "prettier --write",
"test": "jest",
"prepare": "husky"
},
"lint-staged": {
"src/components/**/*.{ts,tsx}": [
"yarn lint-fix",
"yarn format-fix"
],
"src/components/**/*.tsx": "yarn test --findRelatedTests --bail"
},
"devDependencies": {
"@babel/core": "^7.26.10",
"@babel/preset-env": "^7.26.9",
"@babel/preset-react": "^7.26.3",
"@babel/preset-typescript": "^7.26.0",
"@eslint/js": "^9.19.0",
"@rollup/plugin-commonjs": "^28.0.2",
"@rollup/plugin-node-resolve": "^16.0.0",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "11.1.6",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.2.0",
"@types/jest": "^29.5.14",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"eslint": "^9.19.0",
"eslint-plugin-react": "^7.37.4",
"husky": "^9.1.7",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jest-styled-components": "^7.2.0",
"lint-staged": "^15.5.0",
"prettier": "^3.4.2",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"rollup": "^4.30.1",
"rollup-plugin-dts": "^6.1.1",
"rollup-plugin-peer-deps-external": "^2.2.4",
"styled-components": "^6.1.14",
"typescript": "^5.7.3",
"typescript-eslint": "^8.23.0"
},
"peerDependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0",
"styled-components": "^6.1.14"
}
}
CHANGELOG file
Currently, the CHANGELOG.md
looks like this:
## 0.3.0
_Mar. 24, 2025_
- setup jest and testing-library
- add components tests
## 0.2.0
_Fev. 24, 2025_
- setup typescript-eslint and prettier
- add custom rules
## 0.1.0
_Jan. 29, 2025_
- initial config
Since a new version will be released, the changes will be added to the file:
## 0.4.0
_Abr. 29, 2025_
- setup husky and lint-staged
- define pre-commit actions
## 0.3.0
_Mar. 24, 2025_
- setup jest and testing-library
- add components tests
## 0.2.0
_Fev. 24, 2025_
- setup typescript-eslint and prettier
- add custom rules
## 0.1.0
_Jan. 29, 2025_
- initial config
Folder structure
The folder structure is currently as follows. In addition to the modification of some files, the .husky
folder with the pre-commit
file was also added:
Publication of a new version
The first step is to check if the rollup build runs successfully. To do this, run yarn build
in the terminal, as defined in package.json
. If the build is successful, proceed to publish the new version of the library with: npm publish --access public
Conclusion
The goal of this article was to setup Husky and lint-staged to define pre-commit rules for the application. The objective is to ensure that code changes adhere to lint and prettier standardization rules, and validate that the tests continue to pass before finalizing the commit. Here is the repository on github and the library on npmjs with the new modifications.