Next.js App Routerの場合componentはどこに置けばいいの?
きっかけ
私は今まで Pages Router でプロジェクトの開発をしてきました。今の所、具体的な新規で開発する案件の話はないけど今後 App Router で開発するときにどうゆうフォルダ構成にすれば良いか悩んだからまとめようかなと思ったのがきっかけです。
環境について
node: v18.18.2
npm: 9.8.1
react: 18 系
next.js: 13.5.6
具体的にどんなことに悩んだのか
- /app の中で style や util 系関数や component なんかも入れた方がいいの?
- それとも/app を/pages と同じように扱う方がいいの?
この 2 点です。Private Folders やら Route Groups なんかもあったりして Next.js のファイルベースルーティングはいろんなことが柔軟にできるようになった印象です。
しかし、柔軟だからこそ「いや、まじでどうするのが正解なん?」ってなっているのも事実。
実際、Next.js 公式もプロジェクトファイルの整理方法や配置方法
については自由にやってよってスタンスです。
もちろん例は記載してくれていますが。
フォルダ構成の前提
色々試したりしたんですが、フォルダ構成は個人的に良さそうなものを考えてみました。ちなみに component の配置やフォルダ構成の基本はこれをベースにしています。component に限らず styles や types などの配置も一緒に考えます。
├── common
│ ├── constants
│ ├── hooks
│ ├── styles
│ ├── types
│ └── utils
├── components
│ ├── functional
│ ├── layouts
│ ├── ui-elements
│ └── ui-parts
└── features
個人的に考えたフォルダ構成 2 パターン
パターンとしては下記の 2 パターンかなと思います。
-
/src
の中の/app
と同階層に component などを配置する -
/src
を作らずプロジェクト直下に/app を作りその中に構成する
/src
の中の /app
と同階層に component などを配置する
おそらくこれが一番シンプルな構成です。というのも next のプロジェクトを作成するときに src
を作るかどうか聞かれます。基本的にはそのままエンターキーで src ディレクトリが作られる仕様になっています。
$ npx create-next-app@latest --ts
✔ What is your project named? … app-router-src
✔ 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
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
また alias の設定もこの時点でできています。tsconfig.json
を見るとsrc
配下を見るようになっていますね。もちろん /src/app
の中に component や style などを配置していってもいいんですけど、それだとなんか気持ち悪いというか /src
ある意味なくない?って個人的にはなってしまうのでしないかなーと。
ただ、tailwind の設定を変える必要があります。今回の場合だと /features
を足す必要があります。
import type { Config } from "tailwindcss";
const config: Config = {
content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
+ "./src/features/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
backgroundImage: {
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
"gradient-conic":
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
},
},
},
plugins: [],
};
export default config;
以前までの Pages Router とほぼ同じような構成にすることができるので、慣れている部分もあるしシンプルでわかりやすい構成かなと思います。
├──src
├── app
├── common
│ ├── constants
│ ├── hooks
│ ├── styles
│ ├── types
│ └── utils
├── components
│ ├── functional
│ ├── layouts
│ ├── ui-elements
│ └── ui-parts
└── features
/src
を作らずプロジェクト直下に/app を作りその中に構成する
こちらはプロジェクトの直下に /app
を配置します。なので next プロジェクト作成時に src を使わないようにしないといけません。またこのパターンでは Route Groups と Private Folders を使うことでより見やすくわかりやすいフォルダ構成にすることができます。今度は src を作らないように Would you like to use
src/ directory?
で No
を選びます。
$ npx create-next-app@latest --ts
✔ What is your project named? … app-router-app
✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … Yes
✔ Would you like to use `src/` directory? … No
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to customize the default import alias (@/*)? … No
そうすると alias もプロジェクト直下で設定されていますね。
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
/app
に component やルーティングフォルダーを配置すると下記のようになります。このパターンでは Route Groups と Private Folders を使った方がわかりやすくなります。
├──app
├── _common
│ ├── constants
│ ├── hooks
│ ├── styles
│ ├── types
│ └── utils
├── _components
│ ├── functional
│ ├── layouts
│ ├── ui-elements
│ └── ui-parts
├── _features
└── (routes)
Route Groups
フォルダに ()
をつけることでルーティングとは関係ないグループとしてまとめることができます。イメージしやすいのだと public
とprivate
でディレクトリを分けてログインしなくても見れるページ、ログイン後に見れるページというように分けることもできますね。
├── (public)
│ ├── login
│ ├── works
│ └── users
├── (private)
│ └── setting
URL はというと /login
, /works
, /users
, /setting
というように ()
のディレクトリはルーティングの URL には関係がなくできます。
Private Folders とは
次に Private Folders についてです。これは/_hoge
などのように _
をつけることでルーティングに関係のないディレクトリとすることができる機能です。
そもそも App Router のファイルベースルーティングでは ディレクトリに page.tsx
ある場合に限りそのディレクトリがルーティングに使われるという特徴があります。
例えば下記みたいな感じでもルーティングとしては問題はありません。
├──app
├── accounts
│ └── page.tsx
├── common
│ ├── constants
│ ├── hooks
│ ├── styles
│ ├── types
│ └── utils
├── components
│ ├── functional
│ ├── layouts
│ ├── ui-elements
│ └── ui-parts
├── features
├── users
│ └── page.tsx
└── login
└── page.tsx
ではなぜ Private Folders にしたかというと理由は 2 点あって
- 明示的であること
- ルーティングとそれ以外のフォルダの順番を担保できる
明示的であること
これは _
がついているのはルーティングじゃないんだなってことがわかりやすいですよね。
ルーティングとそれ以外のフォルダの順番を担保できる
上記の例の場合だと /accounts
だけ 上の方にあって他のルーティングに関係するフォルダが下の方になっちゃいます。個人的にこうゆうのめっちゃ嫌いなんですよね。関係するフォルダはまとめるとか近くに配置させたい!
なので例に挙げたフォルダ構成であれば component などのルーティングに関係ないものと ルーティングに関係のある (routes)
を明示的に分けてかつまとめることだできますね。ただ個人的には開発中にそのフォルダ内にあることを意識する必要のない /public
や /node_modules
も alias の設定に含まれるのはちょっと気持ち悪いかなとは思いました。ただまー意識するところではないので開発していて不自由に感じることはないと思います。
├──app
├── _common
│ ├── constants
│ ├── hooks
│ ├── styles
│ ├── types
│ └── utils
├── _components
│ ├── functional
│ ├── layouts
│ ├── ui-elements
│ └── ui-parts
├── _features
└── (routes)
まとめ
いかがでしょう?個人的には新規のプロジェクトで Next.js を使うならおそらくシンプルなパターン 1 を選ぶと思います笑
ほぼほぼ default の設定のままでできちゃいますしね。
Discussion