Linter

Bun's fast, built-in linter for JavaScript, TypeScript, and JSX

Bun ships with a fast, built-in linter. There's nothing to install and nothing to configure.

  • 1,000+ rules from ESLint, typescript-eslint, React, import, unicorn, jsx-a11y, Jest, Vitest, and more
  • Type-aware rules, without the typescript package
  • Auto-fix with --fix
  • Works with your existing ESLint or oxlint config

Run the linter#

terminal
bun lint
1 | export function total(prices: number[]) {
2 |   let sum = 0;
3 |   for (let i = 0; i < prices.length; i--) {
      ^
error: The update clause in this loop moves the variable in the wrong direction.  for-direction
    at src/cart.ts:3:3

5 |   }
6 |
7 |   let tax = 0.2;
          ^
warn: 'tax' is never reassigned. Use 'const' instead.  prefer-const
   at src/cart.ts:7:7

2 problems (1 error, 1 warning)
1 can be fixed with --fix
Linted 1 file [5.17ms]

bun lint checks every .js, .jsx, .ts, and .tsx file in the current directory (plus the .mjs, .cjs, .mts, .cts variants). It skips node_modules and anything in your .gitignore.

To lint specific files, pass paths or globs.

terminal
bun lint src "test/**/*.test.ts"

The exit code is 1 if there are errors and 0 if there are only warnings.

Fix problems#

Many rules can fix what they report. --fix applies those fixes and writes the files.

terminal
bun lint --fix

Default rules#

With no config, bun lint turns on the rules that catch real bugs:

  • Every file: ESLint's recommended rules
  • TypeScript: typescript-eslint's recommended rules
  • React: rules-of-hooks, exhaustive-deps, and jsx-key, if react is in your package.json
  • Tests: leftover test.only, expect() without a matcher, un-awaited .resolves, and a few more, in files that import from bun:test

Likely bugs are errors. Unused code, style suggestions, and code that people often write on purpose, like while (match = re.exec(text)), are warnings. A warning doesn't fail the run.

A few defaults differ from ESLint's, so that ordinary code isn't reported:

  • require() is allowed in TypeScript files. Bun runs import and require() in the same file.
  • A name that starts with _ may be unused.
  • done && done(); and ok ? resolve() : reject(); are fine as statements.
  • A no-break space or a full-width space is only reported in code, not in text, comments or JSX.
  • process.exit() ends a case, and // fall-through or // no break marks one that goes on.
  • {false && <Debug />} is a fine way to switch something off for a while.
  • An array of JSX elements needs keys only where React renders it as a list.
  • import React counts as used in a file with JSX.

Each rule's page says what differs and how to change it.

  • In .d.ts files only the errors run.
  • An eslint-disable comment for a rule that is off is left alone.

Configure rules#

Rules live in bunfig.toml. Every built-in rule works the same way. There are no plugins to install.

bunfig.toml
[lint.rules]
"no-console" = "warn"
"prefer-const" = "off"
"eqeqeq" = ["error", "smart"] # with options
"unicorn/prefer-node-protocol" = "error"
"@typescript-eslint/no-explicit-any" = "off"

An option you don't set keeps its default.

To create the section, run bun lint --init.

A typo is an error, not a silent no-op:

2 | "no-consle" = "warn"
    ^
error: Unknown rule "no-consle"
  Did you mean "no-console"?
    at bunfig.toml:2:1

Find a rule#

Browse all rules, or look one up from the terminal. In most terminals you can also click the rule name in a report.

terminal
bun lint --rules                 # list every rule
bun lint --explain prefer-const  # what it does, examples, options

Turn on a group of rules#

Categories switch on many rules at once: correctness, suspicious, pedantic, perf, style, restriction, nursery.

bunfig.toml
[lint.categories]
suspicious = "warn"
perf = "warn"

Ignore files#

bunfig.toml
[lint]
ignorePatterns = ["vendor/", "**/*.gen.ts"]

Disable a rule in code#

bun lint uses ESLint's comments, so existing ones keep working.

// eslint-disable-next-line no-console -- the CLI prints its version
console.log(version);

/* eslint-disable no-console */
console.log("off until the end of the file");

A comment that no longer disables anything gets a warning, and --fix removes it.

Type-aware rules#

Some bugs only show up with types, like a promise nobody awaits. --type-aware turns on the recommended rules that use them.

terminal
bun lint --type-aware
5 | export function onClick() {
6 |   save("hello");
      ^
error: Promises must be awaited, end with a call to .catch, ...  @typescript-eslint/no-floating-promises
    at src/save.ts:6:3

Types come from bun check, Bun's built-in type checker. It's slower than a plain run, because your code is type checked first. To make it the default:

bunfig.toml
[lint]
typeAware = true

Only want one of them? Turn it on like any other rule. You don't need typeAware.

bunfig.toml
[lint.rules]
"@typescript-eslint/no-floating-promises" = "error"

To report type errors too, like bun check does, set typeCheck = true or pass --type-check.

Git hooks#

--staged lints only the files you're about to commit, so you don't need lint-staged.

.git/hooks/pre-commit
#!/bin/sh
bun lint --staged --fix

--changed lints the files that differ from your default branch. Pass a ref to compare with something else.

terminal
bun lint --changed
bun lint --changed=origin/dev

CI#

In GitHub Actions, bun lint annotates the pull request diff automatically.

.github/workflows/lint.yml
steps:
  - uses: actions/checkout@v4
  - uses: oven-sh/setup-bun@v2
  - run: bun lint --max-warnings 0 # fail on warnings too

For other systems, pick a report format with --format: json, junit, checkstyle, gitlab, sarif, unix.

Migrate from ESLint or oxlint#

Already have an eslint.config.js, .eslintrc, or .oxlintrc.json? bun lint reads it and reports what that tool reports. Change the script and you're done.

package.json
{
  "scripts": {
    "lint": "eslint ." 
    "lint": "bun lint" 
  }
}

If package.json has a lint script, bun lint runs that script. Inside the script, bun lint is the linter.

Rules from built-in plugins run natively. Any other ESLint plugin still runs, in JavaScript. To see which is which, and which packages you can uninstall:

terminal
bun lint --doctor

See the guides: Migrate from ESLint and Migrate from oxlint. For custom rules, see ESLint plugins.

CLI Usage#

bun lint [flags] [files, directories, or globs]

Rules#

--rulestring

Set a rule for this run: --rule 'eqeqeq: [error, smart]'

--type-awareboolean

Also run the rules that need type information

--rulesboolean

List every built-in rule. Add -f json for machine-readable output

--explainstring

Print the documentation for a rule

--initboolean

Add a [lint] section to bunfig.toml

Fixing#

--fixboolean

Apply fixes and write the files

--fix-suggestionsboolean

Also apply suggestions, which may change what the code does

--fix-dry-runboolean

Compute fixes without writing them. The json format includes the fixed code

Files#

--stagedboolean

Lint only the files staged in Git

--changedstring

Lint only the files that differ from a Git ref. Defaults to the default branch

--ignore-patternstring

Skip files that match this glob. Can be repeated

--no-ignoreboolean

Lint ignored files too

Output#

-f, --formatstringdefault:pretty

pretty, stylish, json, unix, github, junit, checkstyle, gitlab, sarif, or agent

-o, --output-filestring

Write the report to a file

--quietboolean

Report errors only

--max-warningsnumber

Exit with code 1 if there are more warnings than this

--allboolean

Show every problem. By default, more than 50 identical problems are grouped

Tools#

--doctorboolean

Show which rules of your ESLint or oxlint config run natively, and which packages you can remove

--print-configstring

Print the resolved config for a file

bun lint also accepts the flags of ESLint and oxlint. Run bun lint --help --all to see them.