Biome完全ガイド — ESLint + Prettierを置き換える超高速Rustベースツール


JavaScriptおよびTypeScriptのプロジェクトにおいて、コードの品質担保には長らく「ESLint(リント)+ Prettier(フォーマット)」という2ツール構成が事実上の標準となっていた。しかし、この構成には複雑な設定・プラグイン競合・実行速度の問題が常に付きまとっていた。

2023年に登場した Biome(旧Rome)は、Rustで書かれた単一バイナリツールとして、リント・フォーマット・インポート整理を一括で高速処理する。Prettierのベンチマークと比べて 25倍以上高速 であり、設定も大幅にシンプルになる。

本記事では、Biomeのインストールから実践的な biome.json 設定・VSCode統合・CI/CD連携・既存プロジェクトからの移行まで、2000語を超える詳細解説で網羅する。


1. Biomeとは — ESLint + Prettierとの比較・Rustで超高速

従来ツールチェーンの課題

ESLint + Prettier構成には以下のような課題がある。

設定の複雑さ

最小構成でも .eslintrc.json.prettierrc.eslintignore.prettierignore と複数ファイルが必要になる。TypeScript対応のために @typescript-eslint/parser@typescript-eslint/eslint-plugin を追加し、Reactなら eslint-plugin-reacteslint-plugin-react-hookseslint-plugin-jsx-a11y を追加し、さらにPrettierと競合するESLintルールを無効にするための eslint-config-prettier も必要だ。npm install 後の node_modules を見ると、これらのパッケージだけで数百のサブ依存関係が追加される。

ルール競合

ESLintとPrettierは双方がコードスタイルに関するルールを持つため、設定を誤ると同じルールが相互に干渉してコードが循環修正される問題が起きる。eslint-config-prettier で競合を無効化するのが定石だが、設定の見通しが悪くなる。

実行速度

Node.jsベースのツールは起動オーバーヘッドが大きい。大規模プロジェクトでは eslint . の実行が数分かかることがあり、CIパイプラインのボトルネックになる。

Biomeのアーキテクチャ

Biomeは上記の問題をすべて一度に解決する設計になっている。

単一Rustバイナリ

Biomeの実行コアはRustで書かれており、Node.jsランタイムが不要なネイティブバイナリとして動作する。Rustの所有権モデルによりメモリ安全かつゼロコスト抽象化を実現し、Node.jsより桁違いに高速な処理を可能にする。

統一ツールチェーン

ひとつのツール・ひとつの設定ファイルでリント・フォーマット・インポート整理をすべて処理する。設定は biome.json のみ。

高精度なCST(Concrete Syntax Tree)パーサー

Biomeは独自の高精度CSTパーサーを実装しており、エラーがあってもパースを継続できる(Error Recovery)。これにより構文エラーがあるファイルでも可能な限りリント・フォーマットを実行できる。

パフォーマンス比較

公式ベンチマーク(Prettierのリポジトリ自体を対象に計測)では以下の結果が示されている。

ツール実行時間倍率
Prettier8.9秒
Biome0.35秒25.4×

ESLintとの比較でも大規模プロジェクトでは5〜10倍以上の速度差が報告されている。

Prettierとのフォーマット互換性

BiomeのフォーマッターはPrettier互換を目標として設計されており、出力の 96%以上がPrettierと同一 であることが公式に確認されている。既存プロジェクトをBiomeに移行しても、フォーマット上の差異は最小限に抑えられる。


2. インストール・初期化(biome init)

npmでのインストール

# devDependencyとしてインストール
npm install --save-dev --save-exact @biomejs/biome

# yarnの場合
yarn add --dev --exact @biomejs/biome

# pnpmの場合
pnpm add --save-dev --save-exact @biomejs/biome

# bunの場合
bun add --dev --exact @biomejs/biome

--save-exact フラグを使用しているのは、Biomeがバージョン間でフォーマット出力が変わる可能性があるためだ。バージョンを固定することでCIと開発環境の出力が一致することを保証できる。

初期化

npx @biomejs/biome init

このコマンドを実行すると、プロジェクトルートに biome.json が生成される。

{
  "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
  "vcs": {
    "enabled": false,
    "clientKind": "git",
    "useIgnoreFile": false
  },
  "files": {
    "ignoreUnknown": false,
    "ignore": []
  },
  "formatter": {
    "enabled": true,
    "indentStyle": "tab"
  },
  "organizeImports": {
    "enabled": true
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true
    }
  },
  "javascript": {
    "formatter": {
      "quoteStyle": "double"
    }
  }
}

package.jsonへのスクリプト登録

{
  "scripts": {
    "lint": "biome lint --write .",
    "format": "biome format --write .",
    "check": "biome check --write .",
    "ci": "biome ci ."
  }
}

biome check は lint・format・organizeImports を一括実行するため、通常はこのコマンドをメインで使用する。


3. biome.json設定(linter・formatter・organizeImports)

biome.json はBiomeのすべての設定を一元管理する中心ファイルだ。JSONとJSONc(コメント付きJSON)の両方がサポートされている。

完全設定例

{
  "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",

  "vcs": {
    "enabled": true,
    "clientKind": "git",
    "useIgnoreFile": true,
    "defaultBranch": "main"
  },

  "files": {
    "ignoreUnknown": true,
    "ignore": [
      "dist",
      "build",
      ".next",
      "node_modules",
      "coverage",
      "*.min.js",
      "public/assets"
    ]
  },

  "formatter": {
    "enabled": true,
    "formatWithErrors": false,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineEnding": "lf",
    "lineWidth": 100,
    "attributePosition": "auto"
  },

  "organizeImports": {
    "enabled": true
  },

  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "a11y": {
        "recommended": true,
        "noAccessKey": "error",
        "noAutofocus": "warn",
        "useAltText": "error",
        "useButtonType": "error",
        "useHtmlLang": "error",
        "useLandmarkContent": "warn"
      },
      "complexity": {
        "recommended": true,
        "noBannedTypes": "error",
        "noExtraBooleanCast": "error",
        "noForEach": "warn",
        "noMultipleSpacesInRegularExpressionLiterals": "error",
        "noUselessCatch": "error",
        "noUselessConstructor": "error",
        "noWith": "error",
        "useFlatMap": "warn",
        "useLiteralKeys": "error",
        "useOptionalChain": "warn"
      },
      "correctness": {
        "recommended": true,
        "noConstAssign": "error",
        "noConstantCondition": "error",
        "noEmptyPattern": "error",
        "noInvalidBuiltinInstantiation": "error",
        "noNewSymbol": "error",
        "noSelfAssign": "error",
        "noUndeclaredVariables": "error",
        "noUnreachable": "error",
        "noUnusedImports": "warn",
        "noUnusedVariables": "warn",
        "useArrayLiterals": "error",
        "useExhaustiveDependencies": "warn",
        "useHookAtTopLevel": "error",
        "useIsNan": "error"
      },
      "style": {
        "recommended": true,
        "noDefaultExport": "off",
        "noNegationElse": "warn",
        "noParameterAssign": "error",
        "noRestrictedGlobals": "off",
        "noVar": "error",
        "useBlockStatements": "off",
        "useCollapsedElseIf": "warn",
        "useConst": "error",
        "useDefaultParameterLast": "error",
        "useEnumInitializers": "error",
        "useExplicitLengthCheck": "off",
        "useFilenamingConvention": {
          "level": "warn",
          "options": {
            "strictCase": false,
            "requireAscii": true,
            "filenameCases": ["camelCase", "kebab-case", "PascalCase"]
          }
        },
        "useFragmentSyntax": "warn",
        "useNamingConvention": "off",
        "useSelfClosingElements": "warn",
        "useShorthandAssign": "warn",
        "useSingleVarDeclarator": "off",
        "useTemplate": "warn"
      },
      "suspicious": {
        "recommended": true,
        "noArrayIndexKey": "warn",
        "noAssignInExpressions": "error",
        "noAsyncPromiseExecutor": "error",
        "noCatchAssign": "error",
        "noClassAssign": "error",
        "noCommentText": "error",
        "noCompareNegZero": "error",
        "noConsole": "warn",
        "noConsoleLog": "warn",
        "noControlCharactersInRegex": "error",
        "noDebugger": "error",
        "noDoubleEquals": "error",
        "noDuplicateCase": "error",
        "noDuplicateClassMembers": "error",
        "noDuplicateObjectKeys": "error",
        "noDuplicateParameters": "error",
        "noEmptyBlockStatements": "warn",
        "noExplicitAny": "warn",
        "noFallthroughSwitchClause": "error",
        "noGlobalIsFinite": "error",
        "noGlobalIsNan": "error",
        "noLabelVar": "error",
        "noMisleadingInstantiator": "error",
        "noPrototypeBuiltins": "error",
        "noRedeclare": "error",
        "noShadowRestrictedNames": "error",
        "noUnsafeDeclarationMerging": "error",
        "noUnsafeNegation": "error",
        "useDefaultSwitchClauseLast": "warn",
        "useGetterReturn": "error",
        "useValidTypeof": "error"
      },
      "performance": {
        "recommended": true,
        "noAccumulatingSpread": "warn",
        "noDelete": "warn"
      },
      "security": {
        "noGlobalEval": "error",
        "noDangerouslySetInnerHtml": "warn",
        "noDangerouslySetInnerHtmlWithChildren": "error"
      },
      "nursery": {
        "useSortedClasses": "off"
      }
    }
  },

  "javascript": {
    "formatter": {
      "jsxSingleQuote": false,
      "quoteStyle": "single",
      "trailingCommas": "all",
      "semicolons": "always",
      "arrowParentheses": "always",
      "bracketSpacing": true,
      "bracketSameLine": false,
      "quoteProperties": "asNeeded"
    },
    "parser": {
      "unsafeParameterDecoratorsEnabled": true
    },
    "globals": ["React", "__DEV__", "process"]
  },

  "json": {
    "formatter": {
      "enabled": true,
      "indentStyle": "space",
      "indentWidth": 2,
      "lineWidth": 80,
      "trailingCommas": "none"
    },
    "parser": {
      "allowComments": true,
      "allowTrailingCommas": false
    },
    "linter": {
      "enabled": true
    }
  },

  "css": {
    "formatter": {
      "enabled": true,
      "indentStyle": "space",
      "indentWidth": 2,
      "quoteStyle": "double"
    },
    "linter": {
      "enabled": true
    }
  },

  "overrides": [
    {
      "include": ["**/*.test.ts", "**/*.test.tsx", "**/*.spec.ts"],
      "linter": {
        "rules": {
          "suspicious": {
            "noExplicitAny": "off"
          },
          "correctness": {
            "noUnusedVariables": "off"
          }
        }
      }
    },
    {
      "include": ["scripts/**/*.js", "*.config.js", "*.config.ts"],
      "linter": {
        "rules": {
          "suspicious": {
            "noConsole": "off",
            "noConsoleLog": "off"
          }
        }
      }
    }
  ]
}

vcsセクション

"vcs": {
  "enabled": true,
  "clientKind": "git",
  "useIgnoreFile": true,
  "defaultBranch": "main"
}

useIgnoreFile: true を設定すると、.gitignore の内容をBiomeの除外ファイルとして自動的に利用する。.biomeignore ファイルでBiome専用の除外設定も追加できる。

overridesセクション

特定ディレクトリやファイルパターンに対して設定を上書きできる。テストファイルでは noExplicitAny を無効化する・設定ファイルでは console.log を許可するといった使い方が実践的だ。


4. リント規則(a11y・complexity・correctness・style・suspicious)

Biomeのリント規則は6つのカテゴリに整理されている。

a11y(アクセシビリティ)

WAI-ARIAおよびHTMLアクセシビリティ標準に基づくルール群。

"a11y": {
  "useAltText": "error",          // img要素にalt属性を必須化
  "useButtonType": "error",       // buttonのtype属性を必須化
  "useHtmlLang": "error",         // html要素のlang属性を必須化
  "noAccessKey": "error",         // accesskey属性の使用禁止
  "noAutofocus": "warn",          // autofocus属性の抑制
  "useAriaActivedescendantWithTabindex": "error",
  "useAriaPropsForRole": "error", // roleに対応するARIAプロパティを強制
  "useLandmarkContent": "warn"    // ランドマーク要素にコンテンツを要求
}

実際のエラー例:

// BAD: alt属性なし
<img src="/logo.png" />

// GOOD: alt属性あり
<img src="/logo.png" alt="会社ロゴ" />

// BAD: button type未指定(フォーム内でsubmitとして動作する可能性)
<button onClick={handleClick}>クリック</button>

// GOOD: type明示
<button type="button" onClick={handleClick}>クリック</button>

complexity(複雑度)

コードの複雑度を下げ、可読性を高めるルール群。

"complexity": {
  "noExtraBooleanCast": "error",   // !!xのような冗長なキャストを禁止
  "noForEach": "warn",             // forEach → for...ofへの移行を推奨
  "useOptionalChain": "warn",      // && チェーン → ?. に置換
  "useFlatMap": "warn",            // .map().flat() → .flatMap()
  "useArrowFunction": "warn",      // function式 → アロー関数
  "useLiteralKeys": "error"        // obj["key"] → obj.key
}

自動修正の例:

// Before: noExtraBooleanCast
const isValid = !!user?.name;

// After(自動修正)
const isValid = Boolean(user?.name);

// Before: useOptionalChain
const name = user && user.profile && user.profile.name;

// After(自動修正)
const name = user?.profile?.name;

// Before: useFlatMap
const items = arrays.map(arr => arr.items).flat();

// After(自動修正)
const items = arrays.flatMap(arr => arr.items);

correctness(正確性)

バグを引き起こす可能性のあるパターンを検出するルール群。最も重要なカテゴリ。

"correctness": {
  "noConstAssign": "error",         // const変数への再代入
  "noUndeclaredVariables": "error", // 未宣言変数の使用
  "noUnreachable": "error",         // 到達不能コード
  "noUnusedImports": "warn",        // 未使用のimport
  "noUnusedVariables": "warn",      // 未使用の変数
  "useExhaustiveDependencies": "warn", // React useEffect依存配列
  "useHookAtTopLevel": "error",     // Hooksのトップレベル呼び出しを強制
  "useIsNan": "error"               // NaN比較にisNaN()を強制
}

Reactの依存配列チェック例:

// BAD: count が依存配列に含まれていない
useEffect(() => {
  console.log(count);
}, []); // Biome: useExhaustiveDependencies

// GOOD
useEffect(() => {
  console.log(count);
}, [count]);

style(スタイル)

コードスタイルの統一を促進するルール群。

"style": {
  "noVar": "error",                // var → const/let
  "useConst": "error",             // 再代入されない変数にconst強制
  "useTemplate": "warn",           // 文字列連結 → テンプレートリテラル
  "useFragmentSyntax": "warn",     // <React.Fragment> → <>
  "useSelfClosingElements": "warn" // <div></div> → <div /> (内容空の場合)
}

自動修正の例:

// Before: useTemplate
const message = "Hello, " + name + "! You are " + age + " years old.";

// After
const message = `Hello, ${name}! You are ${age} years old.`;

// Before: useFragmentSyntax
return <React.Fragment><Header /><Main /></React.Fragment>;

// After
return <><Header /><Main /></>;

suspicious(疑わしいコード)

バグの温床になりやすい疑わしいパターンを検出するルール群。

"suspicious": {
  "noDoubleEquals": "error",       // == → === への変換強制
  "noExplicitAny": "warn",         // TypeScript の any 型
  "noDebugger": "error",           // debugger文の禁止
  "noConsole": "warn",             // console使用の抑制
  "noArrayIndexKey": "warn",       // React key={index}の抑制
  "noDuplicateObjectKeys": "error" // オブジェクトの重複キー
}

performance(パフォーマンス)

パフォーマンスに影響するパターンを検出するルール群。

"performance": {
  "noAccumulatingSpread": "warn",  // reduceでのスプレッド累積を警告
  "noDelete": "warn"               // deleteオペレータの使用を警告
}

5. フォーマット設定(インデント・セミコロン・クォート・行長)

Biomeのフォーマッターはグローバル設定と言語別設定の2層構造になっている。

グローバルフォーマット設定

"formatter": {
  "enabled": true,
  "indentStyle": "space",   // "tab" | "space"
  "indentWidth": 2,         // 2 | 4(tab幅またはスペース数)
  "lineEnding": "lf",       // "lf" | "crlf" | "cr"
  "lineWidth": 100,         // 1行の最大文字数(デフォルト80)
  "attributePosition": "auto" // HTML/JSX属性の改行位置
}

indentStyleについて:

Biomeのデフォルトは "tab" だが、多くの既存プロジェクトはスペース2つを使用しているため、移行時は "space" + "indentWidth": 2 に設定することが多い。

JavaScript/TypeScript フォーマット設定

"javascript": {
  "formatter": {
    "quoteStyle": "single",          // "single" | "double"
    "jsxSingleQuote": false,         // JSX属性のクォートスタイル
    "trailingCommas": "all",         // "all" | "es5" | "none"
    "semicolons": "always",          // "always" | "asNeeded"
    "arrowParentheses": "always",    // "always" | "asNeeded"
    "bracketSpacing": true,          // { key: value } のスペース
    "bracketSameLine": false,        // JSX終了ブラケットの位置
    "quoteProperties": "asNeeded"    // オブジェクトキーのクォート
  }
}

trailingCommasの違い:

// "none"
const obj = {
  a: 1,
  b: 2
}

// "es5"(ES5有効な場所のみ)
const obj = {
  a: 1,
  b: 2,  // ← 末尾カンマあり
}

// "all"(関数引数も含む)
function foo(
  param1: string,
  param2: number,  // ← 引数末尾にもカンマ
) {}

semicolonsの違い:

// "always"
const x = 1;
const y = 2;

// "asNeeded"(ASI依存、セミコロン省略)
const x = 1
const y = 2

JSONフォーマット設定

"json": {
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineWidth": 80,
    "trailingCommas": "none"
  }
}

JSONにはトレーリングカンマを含められないため、"none" が唯一の有効値だ。

CSSフォーマット設定

Biome 1.5以降、CSSのフォーマットにも対応した。

"css": {
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineWidth": 100,
    "quoteStyle": "double"
  },
  "linter": {
    "enabled": true
  }
}

6. インポート整理(organizeImports)

BiomeはESLintの import/ordersimple-import-sort プラグインに相当するインポート整理機能を内蔵している。

"organizeImports": {
  "enabled": true
}

整理のルール

Biomeのインポート整理は以下の順序でグループ化・ソートを行う。

  1. ビルトインモジュール(node:プレフィックス or Node.js組み込み)
  2. 外部パッケージ(node_modules
  3. 内部モジュール(相対パス・エイリアス)
// Before(整理前)
import { useState } from 'react';
import fs from 'fs';
import { Button } from '@/components/ui/button';
import { formatDate } from '../../utils/date';
import { useAuth } from '@/hooks/useAuth';
import axios from 'axios';

// After(Biome整理後)
import fs from 'node:fs';

import axios from 'axios';
import { useState } from 'react';

import { Button } from '@/components/ui/button';
import { useAuth } from '@/hooks/useAuth';
import { formatDate } from '../../utils/date';

副作用インポートの保持

副作用インポート(import './styles.css')はソート対象外となり、元の順序が保持される。これは副作用インポートの順序が動作に影響するケースがあるためだ。

// この順序は保持される
import './polyfills';      // 先に実行される必要がある
import './global.css';
import App from './App';

7. VSCode統合(biome拡張機能・format on save)

拡張機能のインストール

VSCode Marketplaceから “Biome”biomejs.biome)をインストールする。

# コマンドラインからインストール
code --install-extension biomejs.biome

.vscode/settings.json の設定

プロジェクトルートに .vscode/settings.json を作成してチーム全員に設定を共有する。

{
  // BiomeをJavaScript/TypeScriptのデフォルトフォーマッターに設定
  "[javascript]": {
    "editor.defaultFormatter": "biomejs.biome",
    "editor.formatOnSave": true
  },
  "[javascriptreact]": {
    "editor.defaultFormatter": "biomejs.biome",
    "editor.formatOnSave": true
  },
  "[typescript]": {
    "editor.defaultFormatter": "biomejs.biome",
    "editor.formatOnSave": true
  },
  "[typescriptreact]": {
    "editor.defaultFormatter": "biomejs.biome",
    "editor.formatOnSave": true
  },
  "[json]": {
    "editor.defaultFormatter": "biomejs.biome",
    "editor.formatOnSave": true
  },
  "[jsonc]": {
    "editor.defaultFormatter": "biomejs.biome",
    "editor.formatOnSave": true
  },
  "[css]": {
    "editor.defaultFormatter": "biomejs.biome",
    "editor.formatOnSave": true
  },

  // 保存時にコードアクション(インポート整理・自動修正)を実行
  "editor.codeActionsOnSave": {
    "quickfix.biome": "explicit",
    "source.organizeImports.biome": "explicit"
  },

  // ESLint拡張機能との競合を避けるため無効化(Biome移行済みプロジェクト)
  "eslint.enable": false
}

推奨拡張機能の設定

.vscode/extensions.json でチームメンバーへの拡張機能インストールを推奨する。

{
  "recommendations": [
    "biomejs.biome"
  ]
}

マルチルートワークスペース対応

モノレポなど複数のプロジェクトを含むワークスペースでは、各プロジェクトディレクトリに biome.json を配置することで、それぞれ異なる設定を適用できる。Biomeは実行ファイルから上位ディレクトリを辿って最初に見つかった biome.json を使用する。


8. CLI使用法(check・format・lint・ci)

BiomeのCLIは4つの主要コマンドを提供する。

biome check

最も汎用的なコマンド。リント・フォーマット・インポート整理を一括実行する。

# 問題の検出(修正なし)
npx biome check .

# 安全な自動修正を適用
npx biome check --write .

# 安全でない修正も含めて適用(注意が必要)
npx biome check --write --unsafe .

# 特定ファイル・ディレクトリのみ
npx biome check src/

# 変更ファイルのみ(Gitステージング済みファイル)
npx biome check --staged .

biome format

フォーマットのみを実行する。

# フォーマット結果をプレビュー(ファイル変更なし)
npx biome format .

# フォーマットを実際に適用
npx biome format --write .

# 特定ファイル
npx biome format --write src/components/Button.tsx

# stdinからの入力を処理
echo "const x={a:1,b:2}" | npx biome format --stdin-file-path test.ts

biome lint

リントのみを実行する。

# リント実行
npx biome lint .

# 安全な自動修正を適用
npx biome lint --write .

# 特定ルールのみ実行
npx biome lint --only=correctness/noUnusedVariables .

# 特定ルールをスキップ
npx biome lint --skip=suspicious/noConsole .

biome ci

CI環境向けコマンド。終了コードでパス/フェイルを返す。--write フラグが使用できず、問題があれば非ゼロで終了する。

npx biome ci .

biome migrate

ESLint・Prettierの設定ファイルをBiomeに移行するコマンド(後述)。

npx biome migrate eslint --write
npx biome migrate prettier --write

診断レベル・出力フォーマット

# JSON形式で出力(他ツールとの連携)
npx biome check --reporter=json .

# GitHubアノテーション形式(GitHub Actionsで使用)
npx biome ci --reporter=github .

# 詳細な診断情報
npx biome check --verbose .

ファイル内でのルール無効化

特定の行やブロックでルールを無効化する場合はコメントを使用する。

// 次の行のルールを無効化
// biome-ignore lint/suspicious/noConsole: デバッグ用
console.log('debug info');

// ブロック全体を無効化
/* biome-ignore lint/correctness/noUnusedVariables: 外部から使用 */
export const _internalHelper = () => {};

// フォーマットを無効化
// biome-ignore format: 手動整形が必要なテーブル
const table = [
  [1, 0, 0],
  [0, 1, 0],
  [0, 0, 1],
];

9. pre-commitフック(lint-staged・husky統合)

コミット前に自動でリント・フォーマットを実行することで、問題のあるコードをリポジトリに混入させない。

husky + lint-staged のセットアップ

# huskyとlint-stagedをインストール
npm install --save-dev husky lint-staged

# huskyの初期化
npx husky init

package.json の設定:

{
  "scripts": {
    "prepare": "husky"
  },
  "lint-staged": {
    "*.{js,jsx,ts,tsx,json,css}": [
      "biome check --write --no-errors-on-unmatched --files-ignore-unknown=true"
    ]
  }
}

.husky/pre-commit:

#!/usr/bin/env sh
npx lint-staged

Biome単体でのpre-commitフック

lint-stagedを使わずBiomeのみで実現する方法もある。

.husky/pre-commit:

#!/usr/bin/env sh

# ステージングされたファイルのみチェック
npx biome check --staged --write .

# 修正されたファイルを再ステージング
git diff --name-only --diff-filter=ACMR | xargs git add

lefthookを使った代替構成

lefthookはhusky + lint-stagedの代替となるツールで、Go製のため高速だ。

npm install --save-dev lefthook

lefthook.yml:

pre-commit:
  parallel: true
  commands:
    biome-check:
      glob: "*.{js,jsx,ts,tsx}"
      run: npx biome check --write --staged {staged_files}
      stage_fixed: true
    biome-json:
      glob: "*.json"
      run: npx biome format --write {staged_files}
      stage_fixed: true

10. TypeScript対応(型チェックとの分離)

Biomeは重要な点でTypeScriptコンパイラ(tsc)とは役割が異なる。

BiomeとTypeScriptコンパイラの分業

機能BiomeTypeScript compiler
構文エラー検出
型エラー検出×
リント(コードパターン)△(一部)
フォーマット×
トランスパイル×

Biomeは型情報を必要としないルールのみを実装している。型情報が必要なルール(例: @typescript-eslint/no-floating-promises)はBiomeでは提供されていない。型チェックには引き続き tsc --noEmit が必要だ。

TypeScript固有のBiomeルール

"typescript": {
  "linter": {
    "enabled": true
  }
}

BiomeはTypeScriptファイルを自動検出し、TypeScript固有の構文を解析する。追加の設定なしにTypeScriptが扱える。

TypeScript固有ルールの例

// noExplicitAny: any型の使用を警告
function process(data: any) { // Biome警告
  return data;
}

// noUnsafeDeclarationMerging: 宣言マージの問題を検出
interface Foo {
  bar: string;
}
class Foo {} // エラー: interfaceとclassの宣言マージ

// useEnumInitializers: enumの値を明示化
enum Direction {
  Up,     // Biome警告: 値を明示すべき
  Down,
}

// 修正後
enum Direction {
  Up = "UP",
  Down = "DOWN",
}

tsconfig.jsonとの連携

BiomeはTypeScriptのパス解決(pathsbaseUrl)を直接読み込まないため、エイリアスパスに対するルールが誤検知することがある。biome.jsonjavascript.globalsoverrides で調整する。

"overrides": [
  {
    "include": ["src/**/*"],
    "javascript": {
      "globals": ["__APP_VERSION__", "__BUILD_DATE__"]
    }
  }
]

11. React/Vue対応(JSX・テンプレート)

React JSXのサポート

BiomeはReact JSXをネイティブにサポートしており、追加プラグインは不要だ。

"javascript": {
  "formatter": {
    "jsxSingleQuote": false,    // JSX属性にダブルクォートを使用
    "bracketSameLine": false    // JSX終了 > を新しい行に配置
  }
}

JSXフォーマット例:

// bracketSameLine: false(デフォルト)
<Button
  variant="primary"
  onClick={handleClick}
  disabled={isLoading}
>
  送信
</Button>

// bracketSameLine: true
<Button
  variant="primary"
  onClick={handleClick}
  disabled={isLoading}>
  送信
</Button>

React固有のリントルール

"correctness": {
  "useExhaustiveDependencies": "warn",  // useEffect依存配列の検証
  "useHookAtTopLevel": "error"           // Hooksのトップレベル呼び出し
},
"suspicious": {
  "noArrayIndexKey": "warn"             // key={index}の警告
},
"style": {
  "useFragmentSyntax": "warn",          // <React.Fragment> → <>
  "useSelfClosingElements": "warn"      // 空要素の自己終了タグ
},
"security": {
  "noDangerouslySetInnerHtml": "warn"  // XSS対策
}

Vueのサポート状況

Biome 1.9時点ではVue Single File Component(.vue)の完全サポートはまだ実験的な段階にある。BiomeはVueファイル内の <script> ブロックのJavaScript/TypeScriptを処理できるが、<template> ブロックのVueLSP固有の構文は処理対象外だ。

Vueプロジェクトでは現状、<template> のリントには eslint-plugin-vue を継続使用し、<script> ブロックのフォーマットのみBiomeに移行するハイブリッド構成も現実的な選択肢だ。


12. ESLint/Prettierからの移行手順

移行コマンドの実行

Biomeは公式の移行ヘルパーコマンドを提供している。

# Prettierの設定を移行
npx biome migrate prettier --write

# ESLintの設定を移行
npx biome migrate eslint --write

# 両方まとめて実行
npx biome migrate prettier --write && npx biome migrate eslint --write

これらのコマンドは既存の .prettierrc.eslintrc.* を読み込み、対応するBiomeルールに変換して biome.json を生成・更新する。

移行ステップ詳細

Step 1: Biomeをインストール

npm install --save-dev --save-exact @biomejs/biome
npx biome init

Step 2: 移行コマンドで設定を変換

npx biome migrate prettier --write
npx biome migrate eslint --write

Step 3: biome.jsonを確認・調整

自動変換で対応できないルール(ESLint固有のプラグインルールなど)はコメントとして biome.json に記録される。手動で対応するBiomeルールを設定するか、不要なものは削除する。

Step 4: フォーマット差分を確認

# 現在のコードに対してBiomeフォーマットを実行
npx biome format --write .

# git diffでフォーマット差分を確認
git diff --stat

Prettierとの互換性は96%以上だが、一部のケースで差異が生じることがある。差異が許容できるか確認する。

Step 5: リント問題を解消

# 自動修正を適用
npx biome check --write .

# 残った問題を手動で修正
npx biome check .

Step 6: ESLint/Prettierの依存関係を削除

npm uninstall eslint prettier \
  @typescript-eslint/eslint-plugin \
  @typescript-eslint/parser \
  eslint-config-prettier \
  eslint-plugin-prettier \
  eslint-plugin-react \
  eslint-plugin-react-hooks \
  eslint-plugin-jsx-a11y \
  eslint-plugin-import

Step 7: 設定ファイルを削除

rm -f .eslintrc.js .eslintrc.json .eslintrc.yaml .eslintrc.cjs
rm -f .prettierrc .prettierrc.js .prettierrc.json .prettierignore
rm -f .eslintignore

Step 8: package.jsonのスクリプトを更新

{
  "scripts": {
    "lint": "biome lint --write .",
    "format": "biome format --write .",
    "check": "biome check --write .",
    "ci": "biome ci ."
  }
}

移行時の注意点

対応していないESLintルール

BiomeはESLintのすべてのルールをカバーしていない。特に型情報を必要とするTypeScript ESLintルールは対応外だ。重要なルールが移行できない場合は、ESLintをBiomeと並行して一部機能に限定して使用する方法もある。

Biome未対応ルールの主な例:

  • @typescript-eslint/no-floating-promises
  • @typescript-eslint/await-thenable
  • import/no-cycle(循環依存の検出)

13. GitHub Actions CI統合

基本的なCI設定

.github/workflows/ci.yml:

name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]

jobs:
  biome:
    name: Biome Check
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run Biome
        run: npx biome ci --reporter=github .

--reporter=github フラグを使用すると、BiomeのエラーがGitHub上のPRのアノテーションとして表示される。

GitHub Actions公式アクションの使用

name: CI

on:
  push:
    branches: [main]
  pull_request:

jobs:
  biome:
    name: Biome Check
    runs-on: ubuntu-latest

    permissions:
      contents: read

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Biome
        uses: biomejs/setup-biome@v2
        with:
          version: latest

      - name: Run Biome CI
        run: biome ci .

biomejs/setup-biome アクションはBiomeバイナリをキャッシュするため、Node.jsのインストールが不要で実行速度が速い。

モノレポ対応CI設定

name: CI

on:
  push:
    branches: [main]
  pull_request:

jobs:
  biome:
    name: Biome Check
    runs-on: ubuntu-latest
    strategy:
      matrix:
        package: [frontend, backend, shared]

    steps:
      - uses: actions/checkout@v4

      - uses: biomejs/setup-biome@v2
        with:
          version: latest

      - name: Run Biome in ${{ matrix.package }}
        run: biome ci packages/${{ matrix.package }}

キャッシュ最適化

- name: Cache Biome binary
  uses: actions/cache@v4
  with:
    path: ~/.cache/biome
    key: biome-${{ runner.os }}-${{ hashFiles('package-lock.json') }}

- name: Install dependencies
  run: npm ci

- name: Run Biome CI
  run: npx biome ci --reporter=github .

PR差分のみをチェック

大規模プロジェクトでCIを高速化するため、変更されたファイルのみをチェックする設定も可能だ。

- name: Get changed files
  id: changed-files
  uses: tj-actions/changed-files@v44
  with:
    files: |
      **/*.{js,jsx,ts,tsx,json,css}

- name: Run Biome on changed files
  if: steps.changed-files.outputs.any_changed == 'true'
  run: |
    npx biome check ${{ steps.changed-files.outputs.all_changed_files }}

biome.jsonのバリデーション

biome.json は構造が複雑になるにつれ、設定ミスが起きやすくなる。スキーマバリデーションを活用することで、設定ファイルのミスを事前に検出できる。

{
  "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json"
}

VSCodeのJSON Language Serverはこのスキーマを利用して biome.json の入力補完・バリデーションを提供する。また、JSON設定ファイルの検証・整形には DevToolBox のJSONフォーマッター・バリデーターが便利だ。biome.json のJSON構造チェック・整形・スキーマとの差異確認をブラウザ上で手軽に行えるため、設定作業の効率が上がる。


パフォーマンス最適化のヒント

.biomeignoreの活用

# .biomeignore
dist/
build/
.next/
.nuxt/
coverage/
node_modules/
**/*.min.js
**/*.d.ts
public/vendor/
storybook-static/

.gitignore を自動参照する設定(vcs.useIgnoreFile: true)と組み合わせることで、対象外ファイルの処理を削減できる。

プロジェクト規模別の推奨設定

小〜中規模プロジェクト(〜5万行):

{
  "linter": {
    "rules": {
      "recommended": true
    }
  }
}

recommended: true のデフォルトルールセットで十分。

大規模プロジェクト(5万行〜):

{
  "linter": {
    "rules": {
      "recommended": true,
      "correctness": {
        "noUnusedImports": "off"
      }
    }
  },
  "overrides": [
    {
      "include": ["**/*.generated.ts"],
      "linter": {
        "enabled": false
      },
      "formatter": {
        "enabled": false
      }
    }
  ]
}

自動生成ファイル(gRPC・GraphQL・OpenAPIから生成されたファイルなど)はリント・フォーマット対象から除外する。


まとめ

BiomeはESLint + Prettierの複雑な設定・依存関係・実行速度の問題をRust製単一ツールで解決する。主なポイントをまとめると:

観点ESLint + PrettierBiome
設定ファイル数4〜8ファイル1ファイル(biome.json)
依存パッケージ数20〜50以上1(@biomejs/biome)
実行速度基準25倍以上高速
TypeScript対応プラグイン必要ネイティブ対応
インポート整理別途プラグイン内蔵
CSSサポートeslint-plugin-css内蔵(1.5+)
移行コストbiome migrate で自動化

2025年以降の新規プロジェクトではBiomeを第一選択として検討することを強く推奨する。既存プロジェクトも biome migrate コマンドで段階的な移行が可能だ。

設定ファイルの管理や biome.json のJSONバリデーションには DevToolBox のJSON関連ツールを活用することで、開発効率をさらに高められる。


参考リソース


関連記事