Next.js開発環境構築(AppRouter, TypeScript, Tailwind CSS対応)

投稿日:

更新日:

はじめに

Next.jsの自分の開発環境が変わったので、備忘録として一連のツール導入までを残しておきます。

開発環境

  • Next.js(App Router)
  • TypeScript
  • Tailwind + shadcn/ui
  • Prettier
  • ESLint
  • Storybook
  • husky + lint-staged

各種設定

Next.js

以下のコマンドを実行してNext.jsのプロジェクトを作成します。

ターミナル
npx create-next-app@latest <プロジェクト名>

質問にはすべてそのままで回答します。

ターミナル
✔ Would you like to use TypeScript? … Yes
✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … Yes
✔ Would you like to use `src/` directory? … Yes
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to customize the default import alias (@/*)? … No

tsconfig.json

お好みですが、以下の記述を追加します。

tsconfig.json
{
  "compilerOptions": {
    // 他の設定...
    "verbatimModuleSyntax": true // 追加
    // 他の設定...
  }
  // 他の設定...
}

また、path aliasとexcludeの設定を追加します。

excludeにshadcn/uiのコンポーネントディレクトリを指定することで、TypeScriptのエラーを回避しています。

tsconfig.json
{
  "compilerOptions": {
    // 他の設定...
    "paths": {
      "@/*": ["./src/*"],
      "@/components/*": ["./src/components/*"],
      "@/lib/*": ["./src/lib/*"],
      "@/stories/*": ["./src/stories/*"],
      "@/styles/*": ["./src/styles/*"]
    }
  }
  // 他の設定...
  "exclude": ["node_modules", "src/components/ui/**/*"]
}

shadcn/ui

以下のコマンドを実行してshadcn/uiをセットアップします。

ターミナル
npx shadcn@latest init

質問には任意の選択肢を選択します。以下はその例です。

ターミナル
✔ Which style would you like to use? › Default
✔ Which color would you like to use as the base color? › Gray
✔ Would you like to use CSS variables for theming? … yes

husky + lint-staged

以下のコマンドを実行しhuskyとlint-stagedをインストールし、.husky/pre-commitにコマンドを追加します。

ターミナル
npm i -D husky lint-staged && npx husky init && echo "npx lint-staged" > .husky/pre-commit

package.jsonに以下の記述を追加します。

package.json
{
  // 他の設定...
  "lint-staged": {}
}

この時点で一度コミットしたい場合は、package.jsonのlint-stagedに何も設定していないので、—no-verifyをつけてコミットしてください。

ターミナル
git commit --no-verify -m "コミットメッセージ"

Prettier

以下のコマンドを実行してprettier関連のプラグインのインストールとファイルを生成します。

ターミナル
npm i -D prettier prettier-plugin-organize-imports && touch .prettierrc && touch .prettierignore

.prettierrcに以下の記述を追加します。

.prettierrc
{
  "printWidth": 80,
  "tabWidth": 2,
  "useTabs": false,
  "semi": true,
  "singleQuote": true,
  "quoteProps": "as-needed",
  "jsxSingleQuote": false,
  "trailingComma": "all",
  "bracketSpacing": true,
  "bracketSameLine": false,
  "arrowParens": "always",
  "plugins": ["prettier-plugin-organize-imports"],
  "overrides": [
    {
      "files": ["*.html"],
      "options": {
        "printWidth": 360
      }
    },
    {
      "files": ["*.css", "*.scss", "*.sass", "*.less"],
      "options": {
        "singleQuote": false
      }
    }
  ]
}

.prettierignoreに以下の記述を追加します。

.prettierignore
# 依存関係
node_modules

# ビルド出力とキャッシュ
.cache/
.next/
.nuxt/
.astro/
build/
dist/
out/

# パッケージマネージャのロックファイル
package-lock.json
yarn.lock
pnpm-lock.yaml

# 特定のJavaScriptモジュールファイル
*.cjs
*.mjs

以下のコマンドを実行し、VS Codeの設定ファイルを作成します。

ターミナル
mkdir -p .vscode && echo '{}' > .vscode/settings.json

.vscode/settings.jsonに以下の記述を追加します。

.vscode/settings.json
{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "editor.formatOnPaste": true,
  "editor.codeActionsOnSave": {
    "source.addMissingImports.ts": "explicit"
  },
  "javascript.preferences.importModuleSpecifier": "non-relative",
  "typescript.preferences.importModuleSpecifier": "non-relative"
}

package.jsonのscriptsに以下の記述を追加します。

package.json
{
  // 他の設定...
  "scripts": {
    // 他の設定...
    "lint:prettier": "prettier --check .",
    "fix:prettier": "npm run lint:prettier -- --write",
    "prepare": "husky"
  },
  // 他の設定...
}

fix:prettierで実行しているコマンドは実質的にprettier —check —writeになります。

ダブルダッシュ(--)により以降の引数を直接コマンドに渡しています。

ESLint

以下のコマンドを実行してESlint関連のプラグインのインストールとファイルを生成します。

ターミナル
npm i -D @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-plugin-tailwindcss eslint-config-prettier && touch .eslintignore

.eslintrc.jsonに以下の記述を追加します。

.eslintrc.json
{
  "extends": [
    "next/core-web-vitals",
    "plugin:@typescript-eslint/recommended",
    "plugin:tailwindcss/recommended",
    "prettier"
  ],
  "parser": "@typescript-eslint/parser",
  "plugins": ["@typescript-eslint"],
  "rules": {
    "@typescript-eslint/consistent-type-imports": [
      "error",
      {
        "prefer": "type-imports",
        "fixStyle": "separate-type-imports"
      }
    ],
    "@typescript-eslint/no-unused-vars": [
      "warn",
      {
        "vars": "all",
        "varsIgnorePattern": "^_",
        "args": "after-used",
        "argsIgnorePattern": "^_"
      }
    ],
    "object-shorthand": "error",
    "tailwindcss/no-custom-classname": "off",
    "react/jsx-curly-brace-presence": "error",
    "react/self-closing-comp": [
      "error",
      {
        "component": true,
        "html": false
      }
    ],
    "@next/next/no-img-element": "off",
    "react/no-unescaped-entities": "off"
  }
}

設定は以下の記事の記述を使用させていただきました。

.eslintignoreに以下の記述を追加します。

.eslintignore
# 依存関係
node_modules/

# ビルド出力とキャッシュ
.cache/
.next/
.nuxt/
.astro/
build/
dist/
out/

# 静的ファイル
public/

# パッケージマネージャのロックファイル
package-lock.json
yarn.lock
pnpm-lock.yaml

# 設定ファイル
vite.config.ts
next.config.js
tsconfig.json
tailwind.config.js
tailwind.config.ts

# 型定義ファイル
src/env.d.ts

# 特定のJavaScriptモジュールファイル
*.cjs
*.mjs

.vscode/settings.jsonに以下の記述を追加します。

.vscode/settings.json
{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "editor.formatOnPaste": true,
  "editor.codeActionsOnSave": {
    "source.addMissingImports.ts": "explicit",
    "source.fixAll.eslint": "explicit" // 追加
  },
  "javascript.preferences.importModuleSpecifier": "non-relative",
  "typescript.preferences.importModuleSpecifier": "non-relative",
  // ▽ 追加
  "eslint.validate": [
    "html",
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact"
  ]
}

package.jsonのscriptsに以下の記述を追加します。

package.json
{
  // 他の設定...
  "scripts": {
    // 他の設定...
    "lint:eslint": "eslint .", // 追加
    "lint:prettier": "prettier . --check",
    "fix:eslint": "npm run lint:eslint -- --fix", // 追加
    "fix:prettier": "npm run lint:prettier -- --write",
    "prepare": "husky"
  },
  // 他の設定...
}

npm-run-all

コマンド1つでまとめて複数のコマンドを実行できるようにするためにnpm-run-allをインストールします。

ターミナル
npm i -D npm-run-all

package.jsonのscriptsに以下のように変更・追加します。

ターミナル
{
  // 他の設定...
  "scripts": {
    // 他の設定...
    "lint": "next lint", // 削除
    "lint": "run-p -l -c --aggregate-output lint:*", // 追加
    "lint:prettier": "prettier . --check",
    "fix": "run-s fix:prettier fix:eslint", // 追加
    "fix:eslint": "npm run lint:eslint -- --fix",
    "fix:prettier": "npm run lint:prettier -- --write",
    "prepare": "husky"
  },
  // 他の設定...
}
  • run-p

    npm-run-allの並列実行(parallel)

    npm run all -pの省略

  • -l

    ラベルを付けて出力を表示し、どのタスクからの出力かを明示

    --print-labelの省略

  • -c

    エラーが発生しても他のタスクを継続して実行

    --continue-on-errorの省略

  • --aggregate-output

    すべてのタスクの出力を集約して表示

  • lint:*

    “lint:”で始まるすべてのスクリプトを実行

  • run-s

    npm-run-allの逐次実行(sequential)

    npm run all -sの省略

lint-stagedの設定

package.jsonのlint-stagedに以下の記述を追加します。

package.json
{
  // 他の設定...
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{json,md,mdx,css,scss,yaml,yml}": [
      "prettier --write"
    ]
  }
}

Storybook

以下のコマンドを実行してStorybookのプロジェクトをセットアップします。

ターミナル
npx storybook@latest init

tsconfig.jsonのpath aliasが反映されるように.storybook/main.tsを以下のように変更します。

.storybook/main.ts
import type { StorybookConfig } from '@storybook/nextjs';
import path from 'path';

const config: StorybookConfig = {
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  addons: [
    '@storybook/addon-onboarding',
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@chromatic-com/storybook',
    '@storybook/addon-interactions',
  ],
  framework: {
    name: '@storybook/nextjs',
    options: {},
  },
  webpackFinal: async (config) => {
    if (config.resolve) {
      config.resolve.alias = {
        ...config.resolve.alias,
        '@': path.resolve(__dirname, '../src'),
      };
    }
    return config;
  },
};
export default config;

そのままだとshadcn/uiのスタイルが適用されないので、.storybook/preview.tsに以下の記述を追加します。

.storybook/preview.ts
import type { Preview } from '@storybook/react';
import '@/styles/globals.css'; // 追加

const preview: Preview = {
  parameters: {
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/i,
      },
    },
  },
};

export default preview;

package.jsonのlint-stagedも以下のように修正します。

package.json
{
  // 他の設定...
  "lint-staged": {
    "*.{js,jsx,ts,tsx,stories.js,stories.jsx,stories.ts,stories.tsx}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{json,md,mdx,css,scss,yaml,yml}": [
      "prettier --write"
    ]
  }
}

動作確認

上記の完了後に変更内容をコミットし、コミット前にPrettierやESLintが実行されることを確認してください。

参考・引用

この記事をシェアする