📟

Next.jsの/pages内でページ以外のコンポーネントを配置する方法

2021/02/17に公開

結論

next.config.jspageExtensions オプションを設定すると良いです 💪

next.config.js
module.exports = {
  // ページコンポーネントの拡張子を変更する
  // 以下の設定の場合、ページコンポーネントは pages/index.page.tsx などのようになる
  pageExtensions: ['page.tsx', 'page.ts']
}

具体的な解説

デフォルトの設定では、/pages内のコンポーネントは全てページを表示するためのコンポーネントとして扱われます。例えば以下のような感じ。

/pages
.
├── _app.tsx
├── header.tsx <- index.tsx内でしか使ってないコンポーネントのファイル
└── index.tsx <- ページコンポーネント
pages/index.tsx
import { Header } from "./header";

const HomePage = () => (
  <>
    {/* ... */}

    {/* ここで普通のコンポーネントとして使っている */}
    <Header />

    {/* ... */}
  </>
)

export default HomePage;
pages/header.tsx
/**
 * @description HomePageでしか使わないヘッダー
 * @note ページコンポーネントではない!
 **/
export const Header = () => (
  <header>
    {/* ... */}
  </header>
)

上記のソースコードを実行すると、header.tsx がページコンポーネントとして扱われてしまい、http://localhost:3000/header にアクセスすると 404 Not Found にならずにエラーが発生してしまいます。

これの回避策としては、header.tsx/pages から移動する事なんですが、問題なのは header.tsxindex.tsx でしか使われていないので、別の所に移動するとソースコードが分散してしまって、後から読むときにフォルダーを行ったり来たりしないといけなくなります。

ソースコードが少なければそれで大丈夫ですが、大きなプロジェクトだとファイルを探すのも大変ですし、エディターの機能を使ってもファイルを見失う事がよくあるので、ただ /pages から移動するだけなのは、あまりやりたくない訳です。

なので、next.config.jspageExtensions オプションを設定して、ページコンポーネントの拡張子を変更する事によって、/pages内にページコンポーネント以外のコンポーネントを置けるようにします。

以下のように、next.config.jsを設定しましょう。

next.config.js
module.exports = {
  // ページコンポーネントの拡張子を変更する
  pageExtensions: ['page.tsx', 'page.ts']
}

上記のように設定したら、以下のようにファイル名を変更します。

/pages
.
├── _app.page.tsx  <- _app.tsxから変更
├── header.tsx     <- ページコンポーネントでは無いので、変更なし!
└── index.page.tsx <- index.tsxから変更

上記の変更をして実行をすると、http://localhost:3000/header にアクセスしても 404 Not Found が表示されるようになります。

これによって、

  • ページコンポーネント -> .page.tsx
  • ページコンポーネント以外のコンポーネント -> .tsx

で、棲み分けることが出来るようになりました。

めでたしめでたし 👏

高度な使い方

フォルダー構造のちょっとした知見をここで共有したいと思います 🧭

先ず TypeScript では、index.tsxまたはindex.tsフォルダー名でインポートすることが出来ます。

.
├── example
|   └── index.ts
└── hoge.tsx
example/index.ts
export const message = "Example内のindex.tsファイルです"
hoge.tsx
// フォルダー名のみ指定しているが、example/index.tsの値をインポートできている
import { message } from "./example";

console.log( message ); // 出力結果 : Example内のindex.tsファイルです

上記の挙動を利用して、Next.js の/pagesでもコンポーネントをフォルダーとして管理することが出来ます。

/pages
.
├── hoge
|   └── index.page.tsx
├── _app.page.tsx
└── index.page.tsx

ここまでの事を応用して、最終的に上記のインポートの挙動と今回のpageExtensionsオプションを組み合わせることで、もっと分かりやすいコンポーネント構造にすることが出来ます。以下はサンプルのフォルダー構造です 👇

/pages
.
├── hoge
|   ├── components <- hoge内のみで使われているコンポーネントをまとめるフォルダー
|   |   ├── footer.tsx
|   |   └── header.tsx
|   └── index.page.tsx
|
├── _app.page.tsx
|
└── index.page.tsx

上記のフォルダー構造にすることで、/pages/hoge内にコンポーネントをたくさん作っても、一目で依存関係が分かりやすくて管理しやすいですし、.module.cssファイルや、別のファイルをフォルダー内に入れる事も出来るので、コンポーネントの拡張性も上がっています。

StoryBookやテストファイルなどが入れやすいので、私は良く多用しています 👺

参考

この記事は、以下の issues を参考にさせてもらっています 👨‍💻

https://github.com/vercel/next.js/issues/8454

あとがき

ここまで読んでくれてありがとうございます 🙏
記事に間違いなどがあれば、コメントなどで教えて頂けると嬉しいです。

これが誰かの参考になれば幸いです。
それではまた 👋

GitHubで編集を提案

Discussion