Biome and Lefthook: two tools to replace your whole linting setup
If you have ever bootstrapped a JavaScript or TypeScript project and ended up with
Prettier, ESLint, @typescript-eslint/parser, @typescript-eslint/eslint-plugin,
eslint-config-prettier, Husky, lint-staged, and Commitlint all sitting in your
devDependencies: this could be of interest to you. Biome and Lefthook replace all of that with two
packages, almost no config, and faster runs.
Biome
Biome is a formatter and linter in one, written in Rust. It replaces Prettier for formatting and ESLint (plus the TypeScript plugins) for linting. One package, one config file.
pnpm add -D -E @biomejs/biome
pnpm biome init
biome init drops a biome.json with sensible defaults. A minimal setup looks like
this:
{
"$schema": "https://biomejs.dev/schemas/2.4.16/schema.json",
"formatter": {
"indentStyle": "space",
"indentWidth": 2
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"semicolons": "asNeeded"
}
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
}
}
To check your files and fix what can be fixed automatically:
pnpm biome check . # report issues
pnpm biome check --write . # report and fix
One caveat worth knowing: Biome does not have every ESLint rule. The recommended
ruleset covers the important ones and the list keeps growing, but if you rely on a
specific ESLint plugin, check the Biome docs before switching.
Lefthook
Lefthook is a git hooks manager written in Go. It replaces Husky for running hooks and Commitlint for validating commit messages. Because it’s a compiled binary rather than a Node.js script, it starts up fast and you stop noticing it’s there.
pnpm add -D lefthook
pnpm lefthook install
lefthook install wires things up inside your .git/hooks folder. The configuration
lives in a lefthook.yml at the root of your project:
pre-commit:
parallel: true
jobs:
- name: biome
glob: '*.{js,ts,jsx,tsx,json,css}'
run: pnpm biome check --no-errors-on-unmatched --files-ignore-unknown=true {staged_files}
commit-msg:
jobs:
- name: message-format
run: >
head -1 {1} | grep -qE '^(feat|fix|hotfix|chore)(\(.+\))?: .+'
|| (echo 'Invalid commit message. Expected format: feat|fix|hotfix|chore: short comment' && exit 1)
A few things to notice here. The pre-commit hook runs Biome only on staged files (Lefthook injects them as {staged_files}), so even large projects stay fast. The
commit-msg hook checks that your message matches a format before the commit goes
through. In this example that format is feat|fix|hotfix|chore: short description,
but you can change the regex to whatever convention your team uses.
Putting it together
Once both are installed, a commit looks like this:
git add src/components/header.tsx
git commit -m 'feat: add sticky header'
Lefthook runs Biome on your staged files first. If anything fails the check, the commit stops. If the files are clean, it validates the commit message. Two guardrails, no extra Node.js overhead.
Here is the full list of packages this replaces:
prettiereslint@typescript-eslint/parser@typescript-eslint/eslint-plugineslint-config-prettierhuskylint-stagedcommitlint+ a config package
That is eight or more packages down to two, with faster cold starts and a single config file for each tool.