コンテンツにスキップ
App RouterGetting Startedレイアウトとページ

レイアウトとページ

Next.js ではファイルシステムベースのルーティングが採用されており、フォルダやファイルを使用してルートを定義できます。このページでは、レイアウトとページを作成し、それらの間でリンクする方法について説明します。

ページの作成

ページとは、特定のルートでレンダリングされる UI です。ページを作成するには、app ディレクトリ内に page ファイルを追加し、React コンポーネントをデフォルトエクスポートします。たとえば、インデックスページ(/)を作成するには、次のようになります。

page.js special file
app/page.tsx
export default function Page() {
  return <h1>Hello Next.js!</h1>
}

レイアウトの作成

レイアウトとは、複数のページで共有される UI です。ナビゲーション時、レイアウトは状態を保持し、インタラクティブなままで、再レンダリングされません。

レイアウトは、layout ファイルから React コンポーネントをデフォルトエクスポートすることで定義できます。コンポーネントは children プロップを受け取る必要があり、これはページまたは別の レイアウト にすることができます。

たとえば、インデックスページを子として受け入れるレイアウトを作成するには、app ディレクトリ内に layout ファイルを追加します。

layout.js special file
app/layout.tsx
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        {/* Layout UI */}
        {/* Place children where you want to render a page or nested layout */}
        <main>{children}</main>
      </body>
    </html>
  )
}

上記のレイアウトは、app ディレクトリのルートに定義されているため、ルートレイアウトと呼ばれます。ルートレイアウトは必須であり、html および body タグを含める必要があります。

ネストされたルートの作成

ネストされたルートとは、複数の URL セグメントで構成されるルートのことです。たとえば、/blog/[slug] ルートは 3 つのセグメントで構成されています。

  • / (ルートセグメント)
  • blog (セグメント)
  • [slug] (リーフセグメント)

Next.js では、

  • フォルダは、URL セグメントにマッピングされるルートセグメントを定義するために使用されます。
  • ファイルpagelayout など)は、セグメントに対して表示される UI を作成するために使用されます。

ネストされたルートを作成するには、フォルダを互いにネストできます。たとえば、/blog のルートを追加するには、app ディレクトリ内に blog という名前のフォルダを作成します。次に、/blog を公開可能にするには、page.tsx ファイルを追加します。

File hierarchy showing blog folder and a page.js file
app/blog/page.tsx
// Dummy imports
import { getPosts } from '@/lib/posts'
import { Post } from '@/ui/post'
 
export default async function Page() {
  const posts = await getPosts()
 
  return (
    <ul>
      {posts.map((post) => (
        <Post key={post.id} post={post} />
      ))}
    </ul>
  )
}

フォルダのネストを続けることで、ネストされたルートを作成できます。たとえば、特定のブログ投稿のルートを作成するには、blog 内に新しい [slug] フォルダを作成し、page ファイルを追加します。

File hierarchy showing blog folder with a nested slug folder and a page.js file
app/blog/[slug]/page.tsx
function generateStaticParams() {}
 
export default function Page() {
  return <h1>Hello, Blog Post Page!</h1>
}

フォルダ名を角括弧(例: [slug])で囲むと、動的ルートセグメントが作成され、データから複数のページを生成するために使用されます。例: ブログ投稿、製品ページなど。

レイアウトのネスト

デフォルトでは、フォルダ階層内のレイアウトもネストされるため、children プロップを介して子レイアウトをラップします。特定のルートセグメント(フォルダ)に layout を追加することで、レイアウトをネストできます。

たとえば、/blog ルートのレイアウトを作成するには、blog フォルダ内に新しい layout ファイルを追加します。

File hierarchy showing root layout wrapping the blog layout
app/blog/layout.tsx
export default function BlogLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return <section>{children}</section>
}

上記の 2 つのレイアウトを組み合わせると、ルートレイアウト(app/layout.js)がブログレイアウト(app/blog/layout.js)をラップし、それがブログ(app/blog/page.js)とブログ投稿ページ(app/blog/[slug]/page.js)をラップします。

動的セグメントの作成

動的セグメントを使用すると、データから生成されるルートを作成できます。たとえば、個々のブログ投稿ごとにルートを手動で作成する代わりに、動的セグメントを作成してブログ投稿データに基づいてルートを生成できます。

動的セグメントを作成するには、セグメント(フォルダ)名を角括弧で囲みます: [segmentName]。たとえば、app/blog/[slug]/page.tsx ルートでは、[slug] が動的セグメントです。

app/blog/[slug]/page.tsx
export default async function BlogPostPage({
  params,
}: {
  params: Promise<{ slug: string }>
}) {
  const { slug } = await params
  const post = await getPost(slug)
 
  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </div>
  )
}

動的セグメントと params プロップスについて詳しくは、動的セグメントを参照してください。

動的セグメント内の ネストされたレイアウトでも、params プロップスにアクセスできます。

検索パラメータを使用したレンダリング

サーバーコンポーネントのページでは、searchParams プロップを使用して検索パラメータにアクセスできます。

app/page.tsx
export default async function Page({
  searchParams,
}: {
  searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}) {
  const filters = (await searchParams).filters
}

searchParams を使用すると、ページは動的レンダリングの対象となります。これは、検索パラメータを読み取るために着信リクエストが必要になるためです。

クライアントコンポーネントは、useSearchParams フックを使用して検索パラメータを読み取ることができます。

useSearchParams については、静的レンダリングおよび動的レンダリングルートで詳しく説明しています。

何を使用し、いつ使用するか

  • 検索パラメータを使用してページデータを読み込む必要がある場合(例: ページネーション、データベースからのフィルタリング)は、searchParams プロップを使用します。
  • 検索パラメータがクライアントでのみ使用される場合(例: プロップスで既に読み込まれたリストのフィルタリング)は、useSearchParams を使用します。
  • 簡単な最適化として、コールバックまたはイベントハンドラーnew URLSearchParams(window.location.search) を使用して、再レンダリングをトリガーせずに検索パラメータを読み取ることができます。

ページ間のリンク

ルート間のナビゲーションには、<Link> コンポーネントを使用できます。<Link> は、HTML の <a> タグを拡張した組み込みの Next.js コンポーネントで、プリフェッチクライアントサイドナビゲーションを提供します。

たとえば、ブログ投稿のリストを生成するには、next/link から <Link> をインポートし、コンポーネントに href プロップを渡します。

app/ui/post.tsx
import Link from 'next/link'
 
export default async function Post({ post }) {
  const posts = await getPosts()
 
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.slug}>
          <Link href={`/blog/${post.slug}`}>{post.title}</Link>
        </li>
      ))}
    </ul>
  )
}

知っておくと良いこと: <Link> は、Next.js でルート間をナビゲーションする主な方法です。より高度なナビゲーションには、useRouter フックを使用することもできます。

ルートプロップスヘルパー

Next.js は、ルート構造から params と名前付きスロットを推論するユーティリティ型を公開しています。

  • PageProps: page コンポーネントのプロップス。paramssearchParams を含みます。
  • LayoutProps: layout コンポーネントのプロップス。children および名前付きスロット(例: @analytics のようなフォルダ)を含みます。

これらはグローバルに利用可能なヘルパーであり、next devnext build、または next typegen のいずれかを実行すると生成されます。

app/blog/[slug]/page.tsx
export default async function Page(props: PageProps<'/blog/[slug]'>) {
  const { slug } = await props.params
  return <h1>Blog post: {slug}</h1>
}
app/dashboard/layout.tsx
export default function Layout(props: LayoutProps<'/dashboard'>) {
  return (
    <section>
      {props.children}
      {/* If you have app/dashboard/@analytics, it appears as a typed slot: */}
      {/* {props.analytics} */}
    </section>
  )
}

知っておくと良いこと

  • 静的ルートでは、params{} に解決されます。
  • PagePropsLayoutProps はグローバルヘルパーです。インポートは不要です。
  • 型は next devnext build、または next typegen の実行中に生成されます。