astris.design

[2025年 Next.js v15]ESLint(v9 Flat Config) + Prettierによる開発環境の構築

Next.js(v15)での、ESLint(v9)・Prettierによる開発環境の構築についてまとめます。ESLintはv9で、Flat Config Filesという設定方法に変わったので、これに対応しています。

定番的な必要最低限の設定をしているので、これをベースにカスタマイズしていただければと思います。
初心者の方は、とりあえずこの手順通りに設定すればOKです。

【目次・Next.js v15の開発環境】

バージョン(2025年1月最新)

  • Nodejs: 22.13.0
  • Next.js: 15.1.5
  • ESLint: 9
  • Pretteir: 3.4.2
  • VSCode: 1.96.4

Nextjsのインストール

以下のコマンドで、Next.jsをインストールします。
対話で、TypeScript、ESLint、Tailwind CSS、App Routerを全てYesにします。

npx create-next-app@latest

Prettierのインストールと設定

Prettierは、文末セミコロンの有無やインデント幅のようなコードの見た目を整形(フォーマット)するものです。
コードフォーマッターと呼ばれるもので、コードの処理内容とは無関係です。

Prettierをインストールします。

npm install -D prettier

Tailwind CSSに対応するため、以下をインストールします。

npm install -D prettier-plugin-tailwindcss

prettier.config.mjs(設定ファイル)を作成します。

prettier.config.mjs
export default config = {
  plugins: ['prettier-plugin-tailwindcss'],
  semi: true,
  singleQuote: true,
  trailingComma: 'all',
  tabWidth: 2,
  printWidth: 100,
  endOfLine: 'lf',
};

ESLintの設定

ESLintは、コードを解析し、品質に関わる問題を検知・修正するものです。
静的解析ツールと呼ばれるもので、Prettierと違い、コードの処理内容に関わります。

TypeScriptに対応するため、以下をインストールします。

npm install -D @eslint/js typescript-eslint

PrettierとESLintの競合を防止するため、以下をインストールします。

npm install -D eslint-config-prettier

ESLint上でPrettierを動かす場合は、eslint-plugin-prettierも必要になりますが、これはPrettier公式で既に非推奨になっています。
ここでは、PrettierとESLintは独立させる方針で構築します。
VSCodeの拡張機能で、ファイル保存時に自動的にフォーマットするよう設定するため、わざわざESLintに乗せる必要もないかと。

Tailwind CSSに対応するため、以下をインストールします。

npm install -D eslint-plugin-tailwindcss

あと、importの順序を整列し、使用されていないimportを削除すると、コードの見通しが良くなるため、以下をインストールします。

npm install -D eslint-plugin-import
npm install -D eslint-plugin-unused-imports

Next.jsインストール時に、eslint.config.mjs(設定ファイル)が作成されているので、これを編集します。
(従来の.eslintrc.jsonによる設定は、v10から廃止となるようです。)

eslint.config.mjs
import { dirname } from 'path';
import { fileURLToPath } from 'url';
import { FlatCompat } from '@eslint/eslintrc';
 
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import tailwind from 'eslint-plugin-tailwindcss';
import importPlugin from 'eslint-plugin-import';
import unusedImports from 'eslint-plugin-unused-imports';
import eslintConfigPrettier from 'eslint-config-prettier';
 
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
 
const compat = new FlatCompat({
  baseDirectory: __dirname,
});
 
export default tseslint.config(
  {
    files: ['*.ts', '*.tsx'], // 読み込むファイル
  },
  {
    ignores: ['**/.next/**/*'], // 無視するファイル
  },
  eslint.configs.recommended,
  tseslint.configs.strictTypeChecked,
  tseslint.configs.stylisticTypeChecked,
  ...compat.extends('next/core-web-vitals'),
  ...tailwind.configs['flat/recommended'],
  {
    // @typescript-eslintに関する設定
    languageOptions: {
      parser: tseslint.parser,
      parserOptions: {
        project: true,
        tsconfigRootDir: __dirname,
      },
    },
    rules: {
      '@typescript-eslint/consistent-type-definitions': ['error', 'type'],
      '@typescript-eslint/no-unsafe-assignment': 'off',
      '@typescript-eslint/no-misused-promises': 'off',
    },
  },
  {
    // tailwindcssに関する設定
    settings: {
      tailwindcss: {
        whitelist: ['hidden-scrollbar', '-webkit-scrollbar'],
      },
    },
  },
  {
    // eslint-plugin-importに関する設定
    plugins: {
      import: importPlugin,
    },
    rules: {
      'import/order': [
        'error',
        {
          groups: ['builtin', 'external', 'internal'],
          alphabetize: { order: 'asc', caseInsensitive: true },
          'newlines-between': 'always', // import groups 1行空ける
          pathGroups: [
            { pattern: 'src/components/**', group: 'internal', position: 'before' },
            { pattern: 'src/lib/**', group: 'internal', position: 'before' },
          ],
        },
      ],
      'import/newline-after-import': 'error',
      'import/no-duplicates': 'error',
    },
  },
  {
    // eslint-plugin-unused-importsに関する設定
    plugins: {
      'unused-imports': unusedImports,
    },
    rules: {
      'unused-imports/no-unused-imports': 'error',
    },
  },
  {
    // その他設定
    files: ["src/**/*.{js,jsx,ts,tsx}"],
    linterOptions: {
      reportUnusedDisableDirectives: "error",
    },
    languageOptions: {
      globals: {
        React: "readonly",
      },
    },
    rules: {
      "react/jsx-boolean-value": "error", // JSXの中でのbooleanの使用
      "react/jsx-curly-brace-presence": "error", // JSXの中での余分な{}の使用
    },
  },
  eslintConfigPrettier, // Prettierとの競合防止
);

デフォルトで読み込んでいるnext/core-web-vitalsは、内部的にeslint-config-nextを読み込んでいます。
eslint-config-next内で、next/recommended、react/recommended、react-hooks/recommendedを読み込んでいるため、これらのインストールは不要です。


コマンドの設定

コマンドでフォーマットやリントができるよう、package.jsonの"script"に、formatとlintを追加します。

package.json
"scripts": {
  "dev": "next dev --turbopack",
  "build": "next build",
  "start": "next start -p 3000",
  "format": "npx prettier --write src",
  "lint": "next lint --dir src"
},

VSCodeの設定

ESLint、Prettier、Tailwind CSSの拡張機能をインストールします。

ESLint:画面にErrorやWarningの下線を表示

https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint

Prettier:保存時に自動フォーマット

https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode

Tailwind CSS IntelliSense:保存時のclassの並び替え、class補間等

https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss

.vscode/setting.json(設定ファイル)を作成します。
拡張機能やFlat Configを有効にするよう設定します。

.vscode/setting.json
{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  },
  "eslint.options": {
    "overrideConfigFile": "eslint.config.mjs"
  },
  "editor.quickSuggestions": {
    "strings": true
  }
}

Warningの注意点

注意点が一つあります。
eslint.config.mjsで*.config.mjsをignoreしないと、これらのファイルにエラーの赤線が上部に表示されます。
ただ、これをignoreすると、今度はnpm run lintやnpm run build で「The Next.js plugin was not detected in your ESLint configuration.」というWariningが出力されます。
リントやビルド自体は最後まで通るのですが、Warningが出ると何かモヤモヤするので、自分はignoreせず赤線の方を妥協しています。


まとめ

Next.js(v15)での、ESLint(v9)・Prettierによる開発環境の構築についてまとめました。
ESLintは、v9でFlat Config Filesによる設定方法がデフォルトとなり、v10で従来の.eslintrc.jsonは廃止となるようです。
これからNext.jsを使い始める方は、まずはこの記事の設定を行い、それをベースにカスタマイズしていくといいでしょう。