ESLint + Prettier の設定ベストプラクティス(React / TypeScript)
概要
React プロジェクトで ESLint と Prettier を併用すると、「保存のたびに整形が競合して怒られる」「どちらが何を担当しているのか曖昧なまま設定ファイルが肥大化する」という問題が起きがちです。
この記事では、ESLint はコードの品質(バグの芽・アンチパターン)を、Prettier は見た目(整形)を担当するという役割分担を軸に、両者を競合させずに共存させる設定をハンズオンで組み立てます。ESLint v9 の Flat Config(eslint.config.js)を前提にします。
環境
- Node.js 20 以上
- React 18 / 19
- TypeScript 5 系
- ESLint 9 系(Flat Config)
- Prettier 3 系
そもそもの役割分担
最初に押さえるべきは、ESLint と Prettier の責務を重ねないという原則です。
| ツール | 担当 | 例 |
|---|---|---|
| ESLint | コード品質・ロジック | 未使用変数、useEffect の依存漏れ、== の誤用 |
| Prettier | 整形(フォーマット) | インデント、クォート、セミコロン、行幅 |
かつては ESLint 側にもインデントやクォートを整える「スタイル系ルール」がありましたが、これを有効にすると Prettier と衝突します。そこで 整形は Prettier に一任し、ESLint 側の整形ルールは無効化するのが定石です。この無効化を担うのが eslint-config-prettier です。
インストール
pnpm add -D eslint @eslint/js typescript-eslint \ eslint-plugin-react eslint-plugin-react-hooks \ eslint-config-prettier prettier
@eslint/js— ESLint 公式の推奨ルールセットtypescript-eslint— TS 用パーサ+ルールeslint-plugin-react/eslint-plugin-react-hooks— React 固有ルールeslint-config-prettier— Prettier と衝突する整形ルールをまとめて無効化prettier— 整形本体
Prettier の設定
.prettierrc.json を用意します。設定は「チームで揉めそうな項目だけ」明示し、あとはデフォルトに従うのが保守的で楽です。
{
"semi": true,
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"tabWidth": 2,
"arrowParens": "always"
}
整形対象外にしたいものは .prettierignore に書きます。
dist build coverage pnpm-lock.yaml
ESLint Flat Config を組み立てる
eslint.config.js を作成します。配列は上から順に適用され、後の要素が前の要素を上書きするのがポイントです。そのため eslint-config-prettier は必ず最後に置きます。
// eslint.config.js
import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import react from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
import prettier from 'eslint-config-prettier';
export default tseslint.config(
{ ignores: ['dist', 'build', 'coverage'] },
js.configs.recommended,
...tseslint.configs.recommended,
{
files: ['**/*.{ts,tsx}'],
plugins: {
react,
'react-hooks': reactHooks,
},
languageOptions: {
parserOptions: {
ecmaFeatures: { jsx: true },
},
},
settings: {
react: { version: 'detect' },
},
rules: {
...react.configs.recommended.rules,
...reactHooks.configs.recommended.rules,
'react/react-in-jsx-scope': 'off', // React 17+ の新 JSX Transform
'react/prop-types': 'off', // TypeScript で型を付けるので不要
},
},
{
rules: {
'@typescript-eslint/no-unused-vars': [
'warn',
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
],
},
},
prettier, // ★必ず最後★
);
スクリプトを整える
package.json に、lint と format を分けて登録します。
{
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"format": "prettier --write .",
"format:check": "prettier --check ."
}
}
CI では検査だけしたいので lint と format:check を、ローカルでは lint:fix と format を使う、という切り分けになります。
エディタ連携(VS Code)
.vscode/settings.json に置くとチーム全体で揃います。
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
}
確認方法
- わざと整形が崩れた TSX を書く
pnpm lintを実行 → 整形の乱れでは怒られないことを確認pnpm formatを実行 → 整形が直ることを確認- 競合チェックを実行:
npx eslint-config-prettier eslint.config.js
No rules that are unnecessary or conflict with Prettier were found. と出れば正常です。
注意点
eslint-config-prettierは最後に置く。順番を間違えると整形ルールが復活します。eslint-plugin-prettierは原則不要。整形ズレが lint エラーになり煩雑・低速になります。react/react-in-jsx-scopeは React 17 以降オフでよい。有効のままだと誤検知します。- ルールを盛りすぎない。recommended +数個で始め、摩擦の出た項目だけ追加します。
まとめ
- ESLint = 品質、Prettier = 整形、と責務を重ねないのが出発点
- 競合は
eslint-config-prettierを Flat Config の最後に置くだけで解消 eslint-plugin-prettierは使わず、lint と format を別コマンドで回す- 迷ったら recommended から始め、摩擦の出た箇所だけ足す
この分担を最初に決めておくと、プロジェクトが大きくなっても設定ファイルが破綻しにくくなります。
