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
お好みですが、以下の記述を追加します。
{
"compilerOptions": {
// 他の設定...
"verbatimModuleSyntax": true // 追加
// 他の設定...
}
// 他の設定...
}
また、path aliasとexcludeの設定を追加します。
excludeにshadcn/uiのコンポーネントディレクトリを指定することで、TypeScriptのエラーを回避しています。
{
"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に以下の記述を追加します。
{
// 他の設定...
"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に以下の記述を追加します。
{
"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に以下の記述を追加します。
# 依存関係
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に以下の記述を追加します。
{
"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に以下の記述を追加します。
{
// 他の設定...
"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に以下の記述を追加します。
{
"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に以下の記述を追加します。
# 依存関係
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に以下の記述を追加します。
{
"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に以下の記述を追加します。
{
// 他の設定...
"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に以下の記述を追加します。
{
// 他の設定...
"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を以下のように変更します。
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に以下の記述を追加します。
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も以下のように修正します。
{
// 他の設定...
"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が実行されることを確認してください。