Menu

レイアウトとページ

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

ページの作成

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

page.js スペシャルファイル
app/page.tsx
TypeScript
export default function Page() {
  return <h1>Hello Next.js!</h1>
}

レイアウトの作成

レイアウトは、複数のページ間で共有される UI です。ナビゲーション時に、レイアウトはステータスを保持し、対話的で、再レンダリングされません。

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

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

layout.js スペシャルファイル
app/layout.tsx
TypeScript
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        {/* レイアウト UI */}
        {/* ページまたはネストされたレイアウトをレンダリングしたい場所に children を配置 */}
        <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 ファイルを追加します:

blog フォルダと page.js ファイルを示すファイルの階層構造
app/blog/page.tsx
TypeScript
// ダミーインポート
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 ファイルを追加します:

ネストされた slug フォルダと page.js ファイルを含む blog フォルダを示すファイル階層構造
app/blog/[slug]/page.tsx
TypeScript
function generateStaticParams() {}
 
export default function Page() {
  return <h1>Hello, Blog Post Page!</h1>
}

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

レイアウトのネスト

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

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

ルートレイアウトがブログレイアウトをラップしていることを示すファイル階層構造
app/blog/layout.tsx
TypeScript
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
TypeScript
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 プロップにアクセスできます。

サーチパラムを使用したレンダリング

Server Component ページでは、searchParamsプロップを使用してサーチパラムにアクセスできます:

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

searchParams を使用すると、サーチパラムをリクエストから読み込む必要があるため、ページは動的レンダリングにオプトインされます。

Client Components は useSearchParamsフックを使用してサーチパラムを読み込むことができます。

静的レンダリングおよび動的レンダリングルートでの useSearchParams についてさらに学習してください。

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

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

ページ間のリンク

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

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

app/ui/post.tsx
TypeScript
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 および名前付きスロットを推論するユーティリティタイプを公開します:

  • PagePropsparamssearchParams を含む page コンポーネントのプロップ。
  • LayoutPropschildren および任意の名前付きスロット(例:@analytics のようなフォルダ)を含む layout コンポーネントのプロップ。

これらはグローバルに利用可能なヘルパーであり、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}
      {/* app/dashboard/@analytics がある場合、型指定されたスロットとして表示されます: */}
      {/* {props.analytics} */}
    </section>
  )
}

補足

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