Next.jsのファイルの規約について

記事タイトルとURLをコピーする

こんにちは、アプリケーションサービス部ディベロップメントサービス1課の外崎です。

今回は、Next.jsのApp Routerのファイル構成について解説します。

App Routerについて

Next.jsには、ウェブページを管理するための「ルーター(ウェブアプリケーションでユーザーが特定のURLにアクセスしたときに、適切なページやコンテンツを表示するための仕組み)」という仕組みがあります。このルーターには、「App Router」と「Pages Router」の2種類があります。

App Routerは、Next.js13 導入されたルーティングシステムです。Reactの新機能を使用してアプリを構築するための新しい方法で、新しくアプリを作成する場合は、この「App Router」を使用することをお勧めします。

ファイル規則

Next.jsでは、特定のファイル名を使用することで、アプリケーションのさまざまな部分を簡単に管理することができます。これにより、UIの共通部分や特定のページ、エラーハンドリングなど、さまざまなシナリオに対応したファイル構成が可能です。

ファイル名 説明
layout ルート間で共有されるUI
page ページを定義する
loading 読み込み時間専用のUI
not-found Not Found専用のUI
error エラーハンドリング用UI
global-error グローバルエラーUI
route サーバーサイドAPIエンドポイント
template 再レンダリングされるレイアウトUI
default パラレルルートのフォールバックUI

page.jsについて

page.jsは、ページ(画面)のコンテンツを定義するためのファイルです。

ルーティングの基礎

Next.jsのルーティングは、ファイルシステムに基づいており、ディレクトリにファイルを配置するだけで自動的にルートが生成されます。

ディレクトリ構造

URL

サンプルコード

app/dashboard/ディレクトリに以下のファイルを作成し、スクリプトを記述します。次に、npm run devコマンドを実行して、ブラウザでhttp://localhost:3000/dashboardにアクセスすると、ダッシュボードページの内容が表示されます。

// `app/dashboard/page.tsx`
export default function Page() {
  return <h1>Hello, Dashboard Page!</h1>
}

戻り値(return)の中身を自由に変更することで、ページの内容をカスタマイズできます。

layout.jsについて

layout.jsは複数のページで共有されるレイアウトを定義するためのファイルです。

例えば、app/dashboard/layout.tsxを作成すると、/dashboardディレクトリ配下の全てのページで共通のレイアウトを適用できます。

また、app/layout.tsxはappに含まれる全てのページで共通のレイアウトを適用できます。

サンプルコード

app/dashboard/ディレクトリに以下のファイルを作成して、ブラウザでhttp://localhost:3000/dashboardにアクセスすると、「Dashboard Layout」というタイトルが追加されます。{children}がpage.tsxの内容を表示します。

// `app/dashboard/layout.tsx`
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <section>
      <h1>Dashboard Layout</h1>
      {children}
    </section>
  );
}

app/layout.tsxを作成して、全てのページで共通のレイアウトを適用します。

// `app/layout.tsx`
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <h1>Root Layout</h1>
        {children}
        </body>
    </html>
  )
}

loading.jsについて

loading.jsは、ページ読み込み中に表示されるローディングメッセージを定義するためのファイルです。使用用途としては、API呼び出しやページ遷移時に時間がかかる場合などに利用されます。

サンプルコード

app/loadingディレクトリに以下の2つのファイルを作成して、ブラウザでhttp://localhost:3000/loadingにアクセスすると、ローディングメッセージが表示され、3秒後にローディングページの内容が表示されます。

// `app/loading/loading.tsx`  
export default function Loading() {
    return (
        <>
        <h1>Loading...</h1>
        </>
    )
}
// `app/loading/page.tsx`
// 疑似的に3秒待機する関数 ※プロミスの概念を使用
const sleep = (sec: number) => new Promise((resolve) => setTimeout(resolve, sec));

export default async function LoadingPage() {
    await sleep(3000);

    return (
        <div>
        <p>This is the loading page</p>
        </div>
    )
}

not-found.jsについて

not-found.jsは、ページが見つからない場合に表示されるメッセージを定義するためのファイルです。

サンプルコード

appディレクトリに以下のファイルを作成して、ブラウザで存在しないページにアクセスする(http://localhost:3000/aaaなど)。先程作成したNot Foundページが表示されます。

// `app/not-found.tsx`
import Link from 'next/link'
 
export default function NotFound() {
  return (
    <div>
      <h2>Not Found!!!</h2>
      <p>リクエストが間違ってます。</p>
      {/* ホームに戻るリンク */}
      <Link href="/">Return Home</Link>
    </div>
  )
}

存在しないアドレスへリクエストした結果

error.tsxについて

error.tsxは、エラーページを定義するためのファイルです。

サンプルコード

app/errorHandleディレクトリに以下の2つのファイルを作成して、ブラウザからボタンクリック時にエラーページが表示されます。

// `app/errorHandle/page.tsx`
'use client'
import React, { useState} from "react";

export default function ErrorHandlePage() {
    const [error, setError] = useState(false);

    const handleClick = () => {
        setError(true);
    };

    if (error) {
        // 意図的にエラーをスローする
        throw new Error('エラー内容ほげほげ');
    }

    return (
        <div>
            <h1>Hello, Next.js ErrorHandle!</h1>
            <button
                onClick={handleClick}
            >
                Errorをスロー
            </button>
        </div>
    );
}
// `app/errorHandle/error.tsx`
'use client'

export default function Error({
    error,
    reset,
  }: {
    error: Error & { digest?: string }
    reset: () => void
  }) {
    return (
      <div>
        <h2>Something went wrong!</h2>
        <h2>{ error.message }</h2>
        <button onClick={() => reset()}>再レンダリング</button>
      </div>
    )
  }

Something went wrong!の後に、該当page.tsxからスローされたエラーメッセージが表示されます。再レンダリングボタンをクリックすると、エラーページがリセットされ、再度ページが表示されます。第二引数のreset()は、エラーページをリセットするための関数です。

ボタンクリック前

ボタンクリック後エラーページが表示

global-error.jsについて

概念が理解しきれなかったため、後日追記します。

route.jsについて

route.jsは、ホスティングサーバー上で実行されるWebAPIエンドポイントを定義するためのファイルです。

サンプルコード

app/apiディレクトリに以下のファイルを作成する。

// `app/api/route.tsx`
import { NextRequest, NextResponse } from 'next/server';
export async function GET() {
    // 標準出力をする
    return NextResponse.json({ message: 'Route Handlers Get API' });
}

ダッシュボードページに以下のスクリプトを追加する。

// `app/dashboard/page.tsx`
'use client';

export default function DashboardPage() {
    // API呼び出しをする
    async function getData(){
        const response = await fetch('/api');
        const data = await response.json();
        console.log(data);
    }
    return (
        <div>
        <p>Hello, Dashboard Page!</p>
        <button onClick={getData}>
            Get Data
        </button>
        </div>
    )
}

ブラウザでhttp://localhost:3000/dashboardにアクセスすると、ダッシュボードページにGet Dataボタンが表示されます。ボタンをクリックすると、コンソールに{message: "Route Handlers Get API"}が表示されます。

template.jsについて

template.jsは、ページ遷移時に状態が維持されるlayout.jsとは異なり、再レンダリング時に更新されるテンプレートを定義します。つまり、画面遷移毎にアニメーションなど何かしら処理を加えたい場合に使用されます。

サンプルコード

app/dashboard/template.tsxに以下のファイルを作成して、ブラウザでhttp://localhost:3000/dashboardにアクセスすると、Dashboard Template!!というタイトルが表示されます。

// `app/dashboard/template.tsx`
export default function Template({ children }: { children: React.ReactNode }) {
  return (
    <>
      <h2>Dashboard Template</h2>
      <div>{children}</div>
    </>
  );
}

app/template.tsxを作成して、全てのページで共通のテンプレートを適用します。

// `app/template.tsx`
export default function Template({ children }: { children: React.ReactNode }) {
  return (
    <>
      <h2>Root Template</h2>
      <div>{children}</div>
    </>
  );
}

default.jsについて

概念が理解しきれなかったため、後日追記します。

layoutとdashboardの処理順序

似た処理であるlayouttemplateについて、ルート配下(app/)とページ配下(app/dashboard/など)も含めた処理順序についても説明します。

コードを見せて具体的に説明すると、layouttemplate{children}の部分が子要素(各種ページ)になります。この概念と順序を意識して共通レイアウトを作成することが重要です。

  return (
    <html lang="en">
      <body>
        <h1>Root Layout</h1>
        {/* children部分が子要素 */}
        {children}
      </body>
    </html>
  );

結論

結論から説明すると以下の順序です。動かしてみる場合は、これまでに作成したサンプルコードを参考にしてください。

  1. ルート配下のlayout
  2. ルート配下のtamplate
  3. ページ配下のlayout
  4. ページ配下のtemplate

layoutとtemplateの処理順序

MUIを使用したローディング

MUIとは

MUI(Material UI)は、GoogleのマテリアルデザインをベースにしたReactコンポーネントライブラリです。様々な部品が揃っているので、デザインやプログラミングの知識が少なくても、簡単に見た目の良いUIを作成することができます。

参考:マテリアルUI-概要

今回は、このMUIを使用したより実践的な画面を作成します。

サンプルコード

以下のパッケージを追加する為、package.jsonを編集します。

// package.json
~省略~
  "dependencies": {
    "@emotion/react": "latest",
    "@emotion/styled": "latest",
    "@mui/material": "latest"
  },
~省略~

MUIライブラリをインポートして、以下のように実装します。

参考:Linear indeterminate

// `app/loading/loading.tsx`
import Box from '@mui/material/Box';
import LinearProgress from '@mui/material/LinearProgress';

export default function Loading() {
  return (
    <Box sx={{ width: '100%' }}>
      <LinearProgress />
    </Box>
  );
}

ブラウザでhttp://localhost:3000/loadingにアクセスすると、MUIのローディングバーが表示されます。

まとめ

今回は、Next.jsのApp Routerのファイル構成について解説しました。各種ファイルには、それぞれの役割があり、適切に使い分けることで、実装の手間や管理の手間を省くことができます。ぜひ参考にして、Next.jsアプリケーションの開発を進めてみてください。

外崎 隼斗 (記事一覧)

アプリケーションサービス部ディベロップサービス1課

雑食系

  翻译: