Next.js

Next.jsのディレクトリ構成:pages・app・publicと各ファイルの役割

この記事でわかること

Next.jsのPages Routerのディレクトリ構成を解説。pages・public・components・lib・typesの役割、_appと_document、生成物とApp Routerの違いを整理します。

Next.jsには、ファイル名や配置場所に意味があるディレクトリと、開発者が整理のために作るディレクトリがあります。この違いを押さえると、部品を追加したつもりでページが増えたり、設定ファイルを誤った場所へ置いたりする間違いを減らせます。

この記事ではPages RouterとTypeScriptを使い、ソースをsrc/へ置く構成を説明します。App Routerのapp/とPages Routerのpages/では規則が異なります。既存プロジェクトの構成を確認してから読み進めてください。

構成の全体像

次のツリーは各役割を示す例です。すべてのファイルを最初から用意する必要はありません。

my-next-app/
  public/
    logo.svg
  src/
    pages/
      _app.tsx
      _document.tsx
      index.tsx
      about.tsx
      404.tsx
      posts/
        [id].tsx
      api/
        hello.ts
    components/
      Header.tsx
      ArticleCard.tsx
    lib/
      format-date.ts
    types/
      article.ts
    styles/
      globals.css
      Header.module.css
  next.config.js
  next-env.d.ts
  tsconfig.json
  package.json
  package-lock.json
  node_modules/
  .next/

public/や設定ファイルはプロジェクトのルートへ置きます。src/public/へ移す構成ではありません。src/を使わず、ルートにpages/を配置する構成も可能ですが、ルートのpages/src/pages/を併設して二つを同じように使うことは避けてください。

pages/はURLとページを対応させる

Pages Routerでは、通常のページファイルの位置がURLになります。

  • src/pages/index.tsx/に対応します。
  • src/pages/about.tsx/aboutに対応します。
  • src/pages/posts/[id].tsxは、記事IDを含む動的なURLに対応します。
  • src/pages/404.tsxは、見つからないページの表示をカスタマイズします。
  • src/pages/api/hello.tsは、画面ではなく/api/helloへのHTTPリクエストを処理します。

pages/に一般的な部品や補助ファイルを置くと、意図しないページとして扱われることがあります。再利用する部品はcomponents/などへ分けます。動的ルートを静的出力する場合は、ビルドするパスをgetStaticPathsで列挙するなど、生成可能な構成にする必要があります。

共通設定を担う特殊ファイル

_app.tsxで共通のUIとCSSを設定する

ファイル:src/pages/_app.tsx

import type { AppProps } from 'next/app';
import '../styles/globals.css';

export default function App({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />;
}

Componentには現在のページが渡され、pagePropsにはページ用のデータが入ります。全ページ共通のProviderやレイアウトも、必要な範囲でここへ配置できます。既存のファイルがある場合は、ほかの共通処理を残して変更してください。

Pages Routerの通常のグローバルCSSは_app.tsxから読み込みます。部品に閉じたスタイルにはCSS Modulesを使えます。

ファイル:src/styles/globals.css

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: sans-serif;
  line-height: 1.8;
}

main {
  max-width: 960px;
  margin: 0 auto;
  padding: 24px;
}

_document.tsxはHTML全体の骨組みを扱う

ファイル:src/pages/_document.tsx

import { Html, Head, Main, NextScript } from 'next/document';

export default function Document() {
  return (
    <Html lang="ja">
      <Head />
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

_document.tsxはサーバー側で使われる文書の骨組みです。ページの状態やクリック処理を管理する場所ではありません。各ページのタイトルや説明文は、Pages Routerではnext/headなどでページごとに設定します。

ここでのHeadnext/documentから読み込む部品で、next/headとは役割が異なります。App Routerのlayout.tsxと混同しないようにしてください。

components/lib/types/は整理のために分ける

components/にはカードやヘッダー、lib/には共通関数、types/には共有する型を置くと役割を追いやすくなります。ただし、これらのディレクトリ名自体にNext.jsの特別な動作はありません。

例として、日付表示を補助関数へ分けます。

ファイル:src/lib/format-date.ts

export function formatDate(isoDate: string): string {
  const date = new Date(isoDate);
  if (Number.isNaN(date.getTime())) {
    throw new Error('有効な日時を指定してください');
  }
  return new Intl.DateTimeFormat('ja-JP', {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    timeZone: 'Asia/Tokyo',
  }).format(date);
}

実行環境のタイムゾーンに任せず、表示基準を指定しています。日付だけの文字列と日時文字列をどのように解釈するかは要件で決め、呼び出し側では時差を含むISO形式などの一貫した入力を使います。

ファイル:src/types/article.ts

export type Article = {
  id: string;
  title: string;
  publishedAt: string;
};

types/へ型を置いても、外部APIのJSONを検証したことにはなりません。また、Next.jsの型定義を手作業でこのディレクトリへコピーする必要はありません。next-env.d.tsはNext.jsが生成するため、通常は手編集しません。

ファイル:src/pages/about.tsx

import { formatDate } from '../lib/format-date';
import type { Article } from '../types/article';

const article: Article = {
  id: 'about',
  title: 'このサイトについて',
  publishedAt: '2026-09-19T00:00:00Z',
};

export default function AboutPage() {
  return (
    <main>
      <h1>{article.title}</h1>
      <time dateTime={article.publishedAt}>{formatDate(article.publishedAt)}</time>
    </main>
  );
}

npm run dev/aboutを開くと、型と補助関数を読み込んだページを確認できます。ビルド対象のファイルから参照されたコードが処理されるので、lib/へ置いただけで処理が自動実行されるわけではありません。

public/のファイルはURLで公開される

public/logo.svg/logo.svgとして参照できます。URLにpublicを含めません。秘密の設定ファイルやAPIキーなど、公開したくないものはここへ置かないでください。

画像はnext/imageなどを使って表示できますが、静的出力のoutput: 'export'ではデフォルトの画像最適化サーバーは使えません。images.unoptimizedや独自のローダーなど、公開構成に合わせて設定します。

public/のJavaScriptやCSSは、置いただけでページへ読み込まれません。通常のCSSはソース側からimportし、外部スクリプトなどはnext/scriptで読み込み時期を管理する方法があります。

APIと生成物の境界を理解する

pages/api/ではAPI Routesを定義できますが、データベースへの接続や入力検証は実装する必要があります。ファイルを置くだけで永続化の仕組みが作られるわけではありません。静的出力ではAPI Routesは動かせないため、外部APIなどを別に用意します。

node_modules/はインストールされた依存パッケージ、.next/はNext.jsのビルド・開発用生成物です。直接編集してソースの代わりに使う場所ではありません。依存関係はpackage.jsonとロックファイルで管理します。

output: 'export'を有効にしたビルドではout/へ配信用ファイルが出ます。これはソース用のディレクトリではなく、変更後に再生成して公開する成果物です。

App Routerの構成を読むとき

App Routerではapp/page.tsxapp/layout.tsxapp/not-found.tsxapp/api/.../route.tsなどを使います。Pages Routerの_app.tsx_document.tsxpages/api/をそのまま同じ意味で配置するものではありません。

両ルーターを移行のために併用する構成もありますが、同じURLを両方で定義しないことなどの制約があります。新しい記事や公式サンプルを読むときは、どちらのルーターを対象にしているかを最初に確認します。

関連記事と公式資料

配置の間違いを見つける確認手順

ルーティングされる場所を確認する

表示されるURLはルーターの規則に沿って決まります。共有部品をPages Routerのpages配下へ置くと、ページとして扱われる場合があります。部品や共通処理を置く場所と、公開するページを分けてください。

公開ファイルと秘密情報を区別する

publicへ置いたファイルは配信対象です。APIキーや未公開の個人情報を置かないようにします。環境変数も、名前と読み込む場所によってクライアントへ渡る可能性があるため、生成物を確認してください。

Next.jsとは・ルーターの違い・学習順を確認する

y.
WRITTEN BY

y_ymo10

SEの部屋で、JavaScript・TypeScript・React.js・Next.jsの開発ノートを公開しています。

ほかの開発ノートを読む →