| | |

Biome 19 🧹 Git Hooks — Pre-commit checks with –staged

⏱️ Estimated read: 12 min | |

The --staged flag is the mechanism that limits a Biome check to the files in the Git index, and it is the core of a pre-commit hook that does not waste time on the untouched files. The flag is a check command option, not a ci command option, because the staged files are a local concept—the remote pipeline has no index to read. The distinction matters: the ci command uses the --changed flag against the base branch, and the check command uses the --staged flag against the index . A hook that runs the biome ci on every commit would either fail because the ci command has no --staged flag or check nothing useful because the pipeline has no index.

A pre-commit hook that runs biome check --staged --write on every commit fixes the formatting and the safe lint issues before the commit is created. The --write flag applies the safe fixes, and the --staged flag scopes the check to the files that are about to be committed. The hook must then re-stage the fixed files, because the --write flag modifies the working tree, not the index. The lint-staged tool is the standard mechanism that handles the re-staging, and the Biome documentation recommends it alongside Husky for the JavaScript ecosystem . The manual git add -u is the alternative, and the alternative is the mechanism that the lint-staged automates.

Key point: biome check --staged checks only the files in the Git index, and --write applies the safe fixes. The hook must re-stage the fixed files, and lint-staged is the tool that does it. The ci command uses --changed instead, because the pipeline has no index. The --no-errors-on-unmatched and the --files-ignore-unknown=true flags are the mechanism that keeps the hook robust when the staged files do not match the Biome patterns.


Why the --staged flag is the hook mechanism

The scope problem. A pre-commit hook that runs biome check . checks the whole repository, and the whole-repository check is slow on the large project. The --staged flag is the mechanism that limits the check to the files that the commit will include, and the limit is the reason the hook is fast . The developer who commits the one file does not wait for the thousand-file check, and the wait is the friction that makes the developer disable the hook.

The re-staging problem. The --write flag modifies the working tree, but the commit reads the index. The fixed file in the working tree is not in the index, and the commit would include the unfixed version. The lint-staged tool is the mechanism that re-stages the fixed files after the command runs, and the re-staging is the reason the commit includes the fix . The git add -u after the biome check --write is the manual alternative, and the manual alternative is the mechanism that the lint-staged automates.

The unmatched-file problem. The --staged flag with the --no-errors-on-unmatched flag is the mechanism that suppresses the error when no staged files match the patterns. The suppression is the reason a commit that touches only the documentation or the deletion does not fail on the “no files processed” error . The --files-ignore-unknown=true flag is the second mechanism that ignores the files that Biome does not support, and the ignore is the reason the hook does not fail on the .env or the .lock files.

The partial-staging problem. A developer can stage the part of a file and leave the rest unstaged. The lint-staged tool is the mechanism that stashes the unstaged changes, runs the command on the staged content, and restores the unstaged changes. The stash is the reason the hook checks what the commit will actually include, not the working tree . The manual git stash is the alternative, and the alternative is the error-prone.

The dependency problem. The lint-staged and the Husky are the separate dependencies, and the Lefthook and the pre-commit are the alternatives. The Biome documentation provides the configurations for the three hook managers, and the choice is the dependency against the configuration . The pre-commit hook manager is the Python-based, and the biomejs/pre-commit repository is the hook definition . The choice is the project’s tooling convention, not the Biome’s requirement.


a. The Husky and lint-staged setup

The Husky is the hook manager, and the lint-staged is the staged-file runner. The two are the standard combination for the JavaScript project, and the Biome documentation recommends them together .

# The installation
npm install --save-dev husky lint-staged @biomejs/biome

# The Husky initialization
npx husky init

# The pre-commit hook
echo 'npx lint-staged' > .husky/pre-commit

The npx husky init is the mechanism that creates the .husky/pre-commit file and adds the prepare script to the package.json. The npx lint-staged is the command that the hook runs, and the command is the mechanism that applies the Biome to the staged files .

The lint-staged configuration is the separate piece. The configuration maps the glob patterns to the Biome commands.

// package.json — the lint-staged configuration
{
  "lint-staged": {
    "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}": [
      "biome check --write --no-errors-on-unmatched --files-ignore-unknown=true"
    ]
  }
}

The biome check --write is the mechanism that formats, sorts the imports, and lints the staged files. The --no-errors-on-unmatched is the mechanism that suppresses the error when no files match, and the --files-ignore-unknown=true is the mechanism that ignores the files that Biome does not support . The lint-staged is the mechanism that re-stages the fixed files after the command runs.

The git add -u is the manual alternative. The command is the mechanism that stages the modified files, and the stage is the reason the commit includes the fix. The lint-staged automates the stage, and the automation is the reason the tool is the recommendation.

# The manual hook with the git add
#!/bin/sh
# The Biome check on the staged files.
biome check --write --staged --no-errors-on-unmatched
# The re-stage of the fixed files.
git add -u

The git add -u is the mechanism that stages the modified files. The biome check --write --staged is the mechanism that fixes the staged files, and the git add -u is the mechanism that includes the fixes in the commit . The manual hook is the alternative for the project that does not want the lint-staged dependency.


b. The Lefthook and pre-commit alternatives

The Lefthook is the cross-platform hook manager, and the Biome documentation provides the configuration. The stage_fixed: true is the mechanism that re-stages the fixed files, and the mechanism is the reason the Lefthook is the alternative to the Husky .

# lefthook.yml — the Biome pre-commit hook
pre-commit:
  commands:
    check:
      glob: "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}"
      run: npx @biomejs/biome check --write --no-errors-on-unmatched --files-ignore-unknown=true --colors=off {staged_files}
      stage_fixed: true

The {staged_files} is the mechanism that passes the staged files to the Biome. The --write is the mechanism that applies the fixes, and the stage_fixed: true is the mechanism that re-stages the fixed files . The Lefthook is the alternative for the project that wants the dependency-free hook manager.

The pre-commit is the multi-language hook manager, and the Biome provides the four hooks via the biomejs/pre-commit repository. The biome-check is the hook that formats, organizes the imports, lints, and applies the safe fixes .

# .pre-commit-config.yaml — the Biome hook
repos:
  - repo: https://github.com/biomejs/pre-commit
    rev: v0.1.0 # The version
    hooks:
      - id: biome-check
        additional_dependencies: ["@biomejs/biome@2.3.11"]

The biome-check is the hook that runs the biome check --write --no-errors-on-unmatched --files-ignore-unknown=true on the staged files. The additional_dependencies is the mechanism that pins the Biome version, and the pin is the reason the hook uses the same version as the local tool . The four hooks are the biome-check, the biome-format, the biome-lint, and the biome-ci, and the choice is the scope .


Complete Example Session

# ============================================
# PART 1: THE HUSKY SETUP
# ============================================

npm install --save-dev husky lint-staged @biomejs/biome
npx husky init
echo 'npx lint-staged' > .husky/pre-commit

# The hook runs the lint-staged on every commit.

# ============================================
# PART 2: THE LINT-STAGED CONFIGURATION
# ============================================

# package.json
{
  "lint-staged": {
    "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}": [
      "biome check --write --no-errors-on-unmatched --files-ignore-unknown=true"
    ]
  }
}

# The Biome runs only on the staged files.

# ============================================
# PART 3: THE MANUAL HOOK
# ============================================

#!/bin/sh
biome check --write --staged --no-errors-on-unmatched
git add -u

# The git add -u re-stages the fixed files.

# ============================================
# PART 4: THE LEFTHOOK CONFIGURATION
# ============================================

# lefthook.yml
pre-commit:
  commands:
    check:
      glob: "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}"
      run: npx @biomejs/biome check --write --no-errors-on-unmatched --files-ignore-unknown=true --colors=off {staged_files}
      stage_fixed: true

# The stage_fixed: true re-stages the fixed files.

# ============================================
# PART 5: THE PRE-COMMIT CONFIGURATION
# ============================================

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/biomejs/pre-commit
    rev: v0.1.0
    hooks:
      - id: biome-check
        additional_dependencies: ["@biomejs/biome@2.3.11"]

# The hook runs the Biome check on the staged files.

# ============================================
# PART 6: THE NO-ERRORS-ON-UNMATCHED
# ============================================

biome check --write --staged --no-errors-on-unmatched

# The flag suppresses the error when no files match.

# ============================================
// PART 7: THE FILES-IGNORE-UNKNOWN
// ============================================

biome check --write --staged --files-ignore-unknown=true

// The flag ignores the unsupported file types.

// ============================================
// PART 8: THE PARTIAL STAGING
// ============================================

git add -p file.js
npx lint-staged

// The lint-staged stashes the unstaged changes,
// runs the command on the staged content,
// and restores the unstaged changes.

// ============================================
// PART 9: THE CI VS THE HOOK
// ============================================

// The hook: biome check --staged --write.
// The CI: biome ci --changed.

// The two are the different scopes and the different modes.

// ============================================
// PART 10: THE CHECKLIST
// ============================================

// [ ] The husky and the lint-staged are installed.
// [ ] The pre-commit hook runs the lint-staged.
// [ ] The lint-staged configuration runs the biome check --write.
// [ ] The --no-errors-on-unmatched is set.
// [ ] The --files-ignore-unknown=true is set.
// [ ] The re-staging is handled by the lint-staged or the git add -u.

The ten parts covered the Husky setup, the lint-staged configuration, the manual hook, the Lefthook configuration, the pre-commit configuration, the --no-errors-on-unmatched, the --files-ignore-unknown, the partial staging, the CI versus the hook, and the checklist. The through-line is that the --staged flag is the scope, and the re-staging is the mechanism that includes the fixes.


Quick Reference

The hook commands

CommandPurpose
biome check --staged --writeCheck and fix the staged files
biome check --stagedCheck the staged files, no write
biome check .Check the whole repository
biome ci --changedThe CI variant against the base branch

The hook managers

ManagerConfigurationRe-staging
Husky + lint-stagedlint-staged in package.jsonThe lint-staged
Lefthooklefthook.ymlThe stage_fixed: true
pre-commit.pre-commit-config.yamlThe hook

The flags

FlagPurpose
--stagedThe files in the Git index
--writeApply the safe fixes
--no-errors-on-unmatchedNo error when nothing matches
--files-ignore-unknown=trueIgnore the unsupported files

The CI comparison

ContextCommandScope
The hookbiome check --staged --writeThe index
The CIbiome ci --changedThe base branch diff
The full localbiome check .The whole repository

Best Practices

✅ Do This:

# Use the --staged for the pre-commit hook
biome check --staged --write --no-errors-on-unmatched              // ✅

# Re-stage the fixed files
git add -u                                                         // ✅

# Use the lint-staged for the automatic re-staging
npx lint-staged                                                    // ✅

❌ Don’t Do This:

# Check the whole repository on every commit
biome check .                                                      // ❌

# Use the ci command in the hook
biome ci                                                           // ❌

# Forget the re-staging
biome check --staged --write # the commit includes the unfixed file // ❌

Common Pitfalls

PitfallWhy It HappensFix
The hook is slowThe biome check . is usedUse the --staged flag
The commit includes the unfixed fileThe re-staging is missingUse the lint-staged or the git add -u
The hook fails on the deletionThe --no-errors-on-unmatched is missingAdd the flag
The hook fails on the .envThe --files-ignore-unknown is missingAdd the flag

Real-World Examples

1. The Husky hook

echo 'npx lint-staged' > .husky/pre-commit

2. The lint-staged config

{ "lint-staged": { "*.{js,ts,jsx,tsx}": "biome check --write" } }

3. The manual hook

biome check --write --staged && git add -u

4. The Lefthook config

run: npx @biomejs/biome check --write {staged_files}

5. The pre-commit config

- id: biome-check

6. The unmatched suppression

biome check --staged --no-errors-on-unmatched

7. The unknown ignore

biome check --staged --files-ignore-unknown=true

8. The partial staging

git add -p && npx lint-staged

9. The CI command

biome ci --changed

10. The full check

biome check .

Visual

The Hook Flow

┌──────────────────────────────────────────────────────────────┐
│  THE HOOK FLOW                                               │
│                                                              │
│  The developer commits                                       │
│         │                                                    │
│         ▼                                                    │
│  ┌─────────────────────┐                                    │
│  │ The pre-commit hook │  The husky or the lefthook         │
│  └──────────┬──────────┘                                    │
│             │                                                │
│             ▼                                                    │
│  ┌─────────────────────┐                                    │
│  │ biome check --staged│  The staged files only             │
│  │ --write             │                                    │
│  └──────────┬──────────┘                                    │
│             │                                                │
│             ▼                                                    │
│  ┌─────────────────────┐                                    │
│  │ The re-staging      │  The lint-staged or the git add -u │
│  └──────────┬──────────┘                                    │
│             │                                                │
│             ▼                                                    │
│  ┌─────────────────────┐                                    │
│  │ The commit includes │  The fixed files                   │
│  │ the fix             │                                    │
│  └─────────────────────┘                                    │
└──────────────────────────────────────────────────────────────┘

The Staged Scope

┌──────────────────────────────────────────────────────────────┐
│  THE STAGED SCOPE                                            │
│                                                              │
│  The working tree                                            │
│  ┌─────────────────────┐                                    │
│  │ The file 1 (staged) │  ✓ checked                         │
│  │ The file 2 (staged) │  ✓ checked                         │
│  │ The file 3 (unstaged)│  ✗ not checked                    │
│  │ The file 4 (unstaged)│  ✗ not checked                    │
│  └──────────┬──────────┘                                    │
│             │                                                │
│             ▼                                                    │
│  ┌─────────────────────┐                                    │
│  │ The --staged flag   │  The Git index only                │
│  └─────────────────────┘                                    │
└──────────────────────────────────────────────────────────────┘

The Re-staging

┌──────────────────────────────────────────────────────────────┐
│  THE RE-STAGING                                              │
│                                                              │
│  The working tree: the file is fixed by --write              │
│  The index: the file is the unfixed version                  │
│         │                                                    │
│         ▼                                                    │
│  ┌─────────────────────┐                                    │
│  │ The git add -u      │  The fixed file is staged          │
│  └──────────┬──────────┘                                    │
│             │                                                │
│             ▼                                                    │
│  ┌─────────────────────┐                                    │
│  │ The commit includes │  The fix                           │
│  │ the fix             │                                    │
│  └─────────────────────┘                                    │
└──────────────────────────────────────────────────────────────┘

Summary

ItemValue
The flag--staged
The commandbiome check --staged --write
The re-stagingThe lint-staged or the git add -u
The HuskyThe hook manager
The lint-stagedThe staged-file runner
The LefthookThe alternative hook manager
The CI commandbiome ci --changed

Key takeaways:

  • The --staged flag is the pre-commit mechanism. It limits the check to the files in the Git index, and the limit is the reason the hook is fast. The ci command uses --changed instead, because the pipeline has no index .
  • The --write flag fixes the working tree, not the index. The hook must re-stage the fixed files, and the lint-staged is the tool that does it. The git add -u is the manual alternative .
  • The Husky and the lint-staged are the standard combination. The Husky runs the hook, and the lint-staged runs the Biome on the staged files. The Biome documentation recommends the combination for the JavaScript project .
  • The Lefthook and the pre-commit are the alternatives. The Lefthook uses the stage_fixed: true for the re-staging, and the pre-commit uses the biomejs/pre-commit repository. The choice is the project’s tooling convention .
  • The --no-errors-on-unmatched and the --files-ignore-unknown=true are the robustness flags. The first suppresses the error when no files match, and the second ignores the unsupported file types. The two flags are the reason the hook does not fail on the empty or the unknown files .

Remember: The Biome pre-commit hook is the biome check --staged --write and the re-staging. The --staged flag is the scope, and the re-staging is the mechanism that includes the fixes. The Husky and the lint-staged are the standard combination, and the Lefthook and the pre-commit are the alternatives. The hook is the fast because the scope, and the commit is the clean because the re-staging.

Was this helpful?


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!