🚫💩 lint-staged --- Run tasks like formatters and linters against staged git files and don't let :poop: slip into your code base! See asciinema video > [!Tip] > Do you…
🚫💩 lint-staged

---
Run tasks like formatters and linters against staged git files and don't let :poop: slip into your code base!
bash
npm install --save-dev lint-staged # requires further setup
$ git commit
✔ Backed up original state in git stash (5bda95f)
❯ Running tasks for staged files...
❯ packages/frontend/.lintstagedrc.json — 1 file
↓ *.js — no files [SKIPPED]
❯ *.{json,md} — 1 file
⠹ prettier --write
↓ packages/backend/.lintstagedrc.json — 2 files
❯ *.js — 2 files
⠼ eslint --fix
↓ *.{json,md} — no files [SKIPPED]
◼ Updating Git index again...
◼ Cleaning up temporary files...
See asciinema video

> [!Tip]
> Do you only want to check staged files for errors, but not edit them automatically?
> You might be interested in this simpler shell script: lint-staged.sh.
Table of Contents
- [Why](#why)
- [Installation and setup](#installation-and-setup)
- [Changelog](#changelog)
- [Command line flags](#command-line-flags)
- [Configuration](#configuration)
- [Filtering files](#filtering-files)
- [What commands are supported?](#what-commands-are-supported)
- [Running multiple commands in a sequence](#running-multiple-commands-in-a-sequence)
- [Using JS configuration files](#using-js-configuration-files)
- [Reformatting the code](#reformatting-the-code)
- [Examples](#examples)
- [Frequently Asked Questions](#frequently-asked-questions)
Why
Code quality tasks like formatters and linters make more sense when run before committing your code. By doing so you can ensure no errors go into the repository and enforce code style. But running a task on a whole project can be slow, and opinionated tasks such as linting can sometimes produce irrelevant results. Ultimately you only want to check files that will be committed.
This project contains a script that will run arbitrary shell tasks with a list of staged files as an argument, filtered by a specified glob pattern.
Related blog posts and talks
- Introductory Medium post - Andrey Okonetchnikov, 2016
- Running Jest Tests Before Each Git Commit - Ben McCormick, 2017
- AgentConf presentation - Andrey Okonetchnikov, 2018
- SurviveJS interview - Juho Vepsäläinen and Andrey Okonetchnikov, 2018
- Prettier your CSharp with
dotnet-formatandlint-staged
Installation and setup
To install _lint-staged_ in the recommended way, you need to:
1. Install _lint-staged_ itself:
- npm install --save-dev lint-staged
1. Set up the pre-commit git hook to run _lint-staged_
- Husky is a popular choice for configuring git hooks
- Read more about git hooks here
1. Install some tools like ESLint or Prettier
1. Configure _lint-staged_ to run code checkers and other tasks:
- for example: { "*.js": "eslint" } to run ESLint for all staged JS files
- See [Configuration](#configuration) for more info
Don't forget to commit changes to package.json and .husky to share this setup with your team!
Now change a few files, git add or git add --patch some of them to your commit, and try to git commit them.
See [examples](#examples) and [configuration](#configuration) for more information.
> [!CAUTION]
> _Lint-staged_ runs git operations affecting the files in your repository. By default _lint-staged_ creates a git stash as a backup of the original state before running any configured tasks to help prevent data loss.
Changelog
See Releases.
Migration
For breaking changes, see [MIGRATION.md](./MIGRATION.md).
Command line flags
❯ npx lint-staged --help
Usage: lint-staged [options]
-h, --help display this help message
-V, --version display the current version number
--allow-empty allow empty commits when tasks revert all staged changes (default: false)
-p, --concurrent the number of tasks to run concurrently, or false for serial (default: true)
-c, --config [path] path to configuration file, or - to read from stdin
--continue-on-error run all tasks to completion even if one fails (default: false)
--cwd [path] run all tasks in specific directory, instead of the current
-d, --debug print additional debug information (default: false)
--diff [string] override the default "--staged" flag of "git diff" to get list of files. Implies "--no-stash".
--diff-filter [string] override the default "--diff-filter=ACMR" flag of "git diff" to get list of files
--fail-on-changes fail with exit code 1 when tasks modify tracked files (default: false)
--no-hide-partially-staged hide unstaged changes from partially staged files (default: true)
--hide-unstaged hide all unstaged changes, instead of just partially staged (default: false)
--hide-all hide all unstaged changes and untracked files (default: false)
--max-arg-length [number] maximum length of the command-line argument string (default: 0)
-q, --quiet disable lint-staged's own console output (default: false)
-r, --relative pass relative filepaths to tasks (default: false)
--no-revert revert to original state in case of errors (default: true)
--no-stash enable the backup stash (default: true)
-v, --verbose show task output even when tasks succeed; by default only failed output is shown (default: false)
Any lost modifications can be restored from a git stash:
> git stash list --format="%h %s"
On main: lint-staged automatic backup
> git apply --index
--allow-empty
By default, when tasks undo all staged changes, lint-staged will exit with an error and abort the commit. Use this flag to allow creating empty git commits.
--concurrent [number|boolean]
Controls the [concurrency of tasks](#task-concurrency) being run by lint-staged. NOTE: This does NOT affect the concurrency of subtasks (they will always be run sequentially). Possible values are:
false: Run all tasks seriallytrue(default) : _Infinite_ concurrency. Runs as many tasks in parallel as possible.{number}: Run the specified number of tasks in parallel, where1is equivalent tofalse.
--config [path]
Manually specify a path to a config file or npm package name. Note: when used, lint-staged won't perform the config file search and will print an error if the specified file cannot be found. If '-' is provided as the filename then the config will be read from stdin, allowing piping in the config like cat my-config.json | npx lint-staged --config -.
--cwd [path]
Change the working directory _lint-staged_ runs tasks in. Defaults to process.cwd() and the value can be absolute or relative to the default value.
--debug
Run in debug mode. When set, it does the following:
- log additional information about staged files, commands being executed, location of binaries, etc.
- uses
verboserenderer forlistr2; this causes serial, uncoloured output to the terminal, instead of the default (beautified, dynamic) output.
verbose renderer can also be activated by setting the TERM=dumb or NODE_ENV=test environment variables)
--diff
By default tasks are filtered against all files staged in git, generated from git diff --staged. This option allows you to override the --staged flag with arbitrary revisions. For example to get a list of changed files between two branches, use --diff="branch1...branch2". You can also read more from about git diff and gitrevisions. This option also implies --no-stash.
--diff-filter [string]
By default only files that are _added_, _copied_, _modified_, or _renamed_ are included. Use this flag to override the default ACMR value with something else: _added_ (A), _copied_ (C), _deleted_ (D), _modified_ (M), _renamed_ (R), _type changed_ (T), _unmerged_ (U), _unknown_ (X), or _pairing broken_ (B). See also the git diff docs for --diff-filter.
--continue-on-error
By default _lint-staged_ will "exit early" when any of the configured tasks fails, to make sure the runtime is short. With this flag, _lint-staged_ will instead run all tasks to completion and only fail at the end, allowing all task output to be seen.
--fail-on-changes
By default changes made by tasks are automatically staged and added to the commit. This flag disables the behavior and makes _lint-staged_ exit with code 1, failing the commit instead. Using this flag also implies the --no-revert flag which means any changes made my tasks will be left in the working tree after failing, so that they can be manually staged and the commit tried again.
--max-arg-length [number]
long commands (a lot of files) are automatically split into multiple chunks when it detects the current shell cannot handle them. Use this flag to override the maximum length of the generated command string.
--no-stash
By default a backup stash will be created before running the tasks, and all task modifications will be reverted in case of an error. This option will disable creating the stash, and instead leave all modifications in the index when aborting the commit.
--no-hide-partially-staged
By default, unstaged changes from partially staged files will be hidden and applied back after running tasks. This option will disable this behavior, causing those changes to also be committed.
--hide-unstaged
Use this option to hide all unstaged changes in tracked files, instead of just those which are also partially staged, before running tasks. The changes will be applied back after running the tasks.
--hide-all
Add new option --hide-all for hiding all unstaged changes and untracked files, before running tasks. This makes it easier to run tools like Knip which check for unused code. Untracked files are included in the backup stash and restored automatically after running.
--quiet
Suppress all CLI output, except from tasks.
--relative
Pass filepaths relative to process.cwd() (where lint-staged runs) to tasks. Default is false.
--no-revert
By default all task modifications will be reverted in case of an error. This option will disable the behavior, and apply task modifications to the index before aborting the commit.
--verbose
Show task output even when tasks succeed. By default only failed output is shown.
Configuration
_Lint-staged_ can be configured in many ways:
lint-stagedobject in yourpackage.json, orpackage.yaml.lintstagedrcfile in JSON or YML format, or you can be explicit with the file extension:
.lintstagedrc.json
- .lintstagedrc.yaml
- .lintstagedrc.yml
.lintstagedrc.mjsorlint-staged.config.mjsfile in ESM format
export default { ... }
.lintstagedrc.cjsorlint-staged.config.cjsfile in CommonJS format
module.exports = { ... }
lint-staged.config.jsor.lintstagedrc.jsin either ESM or CommonJS format, depending on
"type": "module" option or not.
- Pass a configuration file using the
--configor-cflag
You can also place multiple configuration files in different directories inside a project. For a given staged file, the closest configuration file will always be used and tasks will by default run in the directory of the config (for example, the directory of a specific package in a monorepo). See ["How to use lint-staged in a multi-package monorepo?"](#how-to-use-lint-staged-in-a-multi-package-monorepo) for more info and an example.
package.json example:
json
{
"lint-staged": {
"*": "your-cmd"
}
}
.lintstagedrc example
json
{
"*": "your-cmd"
}
This config will execute your-cmd with the list of currently staged files passed as arguments.
So, considering you did git add file1.ext file2.ext, lint-staged will run the following command:
your-cmd file1.ext file2.ext
TypeScript
_Lint-staged_ provides TypeScript types for the configuration and main Node.js API. You can use the JSDoc syntax in your JS configuration files:
js
/**
* @filename: lint-staged.config.js
* @type {import('lint-staged').Configuration}
*/
export default {
'*': 'prettier --write',
}
It's also possible to use the .ts file extension for the configuration if your Node.js version supports it. The --experimental-strip-types flag was introduced in Node.js v22.6.0 and unflagged in v23.6.0, enabling Node.js to execute TypeScript files without additional configuration.
shell
export NODE_OPTIONS="--experimental-strip-types"
npx lint-staged --config lint-staged.config.ts
Task concurrency
By default _lint-staged_ will run configured tasks concurrently. This means that for every glob, all the commands will be started at the same time. With the following config, both eslint and prettier will run at the same time:
json
{
"*.ts": "eslint",
"*.md": "prettier --list-different"
}
This is typically not a problem since the globs do not overlap, and the commands do not make changes to the files, but only report possible errors (aborting the git commit). If you want to run multiple commands for the same set of files, you can use the array syntax to make sure commands are run in order. In the following example, prettier will run for both globs, and in addition eslint will run for *.ts files _after_ it. Both sets of commands (for each glob) are still started at the same time (but do not overlap).
json
{
"*.ts": ["prettier --list-different", "eslint"],
"*.md": "prettier --list-different"
}
Pay extra attention when the configured globs overlap, and tasks make edits to files. For example, in this configuration prettier and eslint might try to make changes to the same *.ts file at the same time, causing a _race condition_:
json
{
"*": "prettier --write",
"*.ts": "eslint --fix"
}
You can solve it using the negation pattern and the array syntax:
```json
{
"!(*.ts)": "p
…
Members
-
lint-staged
🚫💩 — Run tasks like formatters and linters against staged git files
JavaScript ★ 15k 18d agoExplain → -
lint-staged.sh
Run linters against files staged in Git
Shell ★ 7 1mo agoExplain →
No repos match these filters.