並列ルート
並列ルートを使用すると、同じレイアウト内で1つ以上のページを同時に、または条件付きでレンダリングできます。これらは、ダッシュボードやソーシャルサイトのフィードなど、アプリの非常に動的なセクションに役立ちます。
たとえば、ダッシュボードを検討すると、並列ルートを使用してteam
ページとanalytics
ページを同時にレンダリングできます。


スロット
並列ルートは、名前付き**スロット**を使用して作成されます。スロットは@folder
規則で定義されます。たとえば、次のファイル構造は、@analytics
と@team
の2つのスロットを定義しています。


スロットは、共有親レイアウトのプロップとして渡されます。上記の例では、app/layout.js
内のコンポーネントは、@analytics
と@team
のスロットプロップを受け入れ、children
プロップとともに並列でレンダリングできます。
export default function Layout({
children,
team,
analytics,
}: {
children: React.ReactNode
analytics: React.ReactNode
team: React.ReactNode
}) {
return (
<>
{children}
{team}
{analytics}
</>
)
}
ただし、スロットはルートセグメントではなく、URL構造には影響しません。たとえば、/@analytics/views
の場合、@analytics
はスロットであるため、URLは/views
になります。スロットは通常のページコンポーネントと組み合わされて、ルートセグメントに関連付けられた最終的なページを形成します。このため、同じルートセグメントレベルで個別の静的スロットと動的スロットを持つことはできません。1つのスロットが動的な場合、そのレベルのすべてのスロットは動的である必要があります。
知っておくと良いこと:
children
プロップは、フォルダーにマップする必要のない暗黙のスロットです。つまり、app/page.js
はapp/@children/page.js
と同等です。
アクティブ状態とナビゲーション
デフォルトでは、Next.jsは各スロットのアクティブな状態(またはサブページ)を追跡します。ただし、スロット内でレンダリングされるコンテンツは、ナビゲーションの種類によって異なります。
- **ソフトナビゲーション**:クライアントサイドナビゲーション中に、Next.jsは部分レンダリングを実行し、スロット内のサブページを変更しますが、現在のURLと一致しなくても、他のスロットのアクティブなサブページは維持されます。
- **ハードナビゲーション**:フルページロード(ブラウザのリフレッシュ)の後、Next.jsは現在のURLと一致しないスロットのアクティブな状態を判断できません。代わりに、一致しないスロットに
default.js
ファイル、またはdefault.js
が存在しない場合は404
をレンダリングします。
知っておくと良いこと:
- 一致しないルートの
404
は、意図していないページに並列ルートを誤ってレンダリングしないようにするのに役立ちます。
default.js
初期ロードまたはフルページリロード時にフォールバックとしてレンダリングするdefault.js
ファイルを定義できます。
次のフォルダー構造を考えてみましょう。@team
スロットには/settings
ページがありますが、@analytics
にはありません。


/settings
に移動すると、@team
スロットは/settings
ページをレンダリングしますが、@analytics
スロットの現在アクティブなページは維持されます。
更新すると、Next.jsは@analytics
にdefault.js
をレンダリングします。default.js
が存在しない場合は、代わりに404
がレンダリングされます。
さらに、children
は暗黙のスロットであるため、Next.jsが親ページのアクティブ状態を復元できない場合のフォールバックをレンダリングするために、default.js
ファイルを作成する必要があります。
useSelectedLayoutSegment(s)
useSelectedLayoutSegment
とuseSelectedLayoutSegments
の両方で、parallelRoutesKey
パラメーターを受け入れます。これにより、スロット内にあるアクティブなルートセグメントを読み取ることができます。
'use client'
import { useSelectedLayoutSegment } from 'next/navigation'
export default function Layout({ auth }: { auth: React.ReactNode }) {
const loginSegment = useSelectedLayoutSegment('auth')
// ...
}
ユーザーがapp/@auth/login
(またはURLバーの/login
)に移動すると、loginSegment
は文字列"login"
になります。
例
条件付きルート
ユーザーロールなどの特定の条件に基づいてルートを条件付きでレンダリングするには、パラレルルートを使用できます。たとえば、/admin
または/user
ロールに対して異なるダッシュボードページをレンダリングする場合などです。


import { checkUserRole } from '@/lib/auth'
export default function Layout({
user,
admin,
}: {
user: React.ReactNode
admin: React.ReactNode
}) {
const role = checkUserRole()
return <>{role === 'admin' ? admin : user}</>
}
タブグループ
ユーザーがスロットを個別に移動できるように、スロット内にlayout
を追加できます。これは、タブを作成する場合に役立ちます。
たとえば、@analytics
スロットには、/page-views
と/visitors
の2つのサブページがあります。


@analytics
内で、2つのページ間でタブを共有するためのlayout
ファイルを作成します。
import Link from 'next/link'
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<>
<nav>
<Link href="/page-views">Page Views</Link>
<Link href="/visitors">Visitors</Link>
</nav>
<div>{children}</div>
</>
)
}
モーダル
パラレルルートは、ルートのインターセプトと組み合わせて使用して、ディープリンクに対応したモーダルを作成できます。これにより、モーダルの構築における一般的な課題(以下のような課題)を解決できます。
- モーダルコンテンツをURLで共有可能にする。
- ページが更新された場合でも、モーダルを閉じずにコンテキストを保持する。
- 前のルートに移動するのではなく、戻るナビゲーションでモーダルを閉じる。
- 進むナビゲーションでモーダルを再度開く。.
ユーザーがクライアント側のナビゲーションを使用してレイアウトからログインモーダルを開いたり、個別の/login
ページにアクセスしたりできるUIパターンを考えてみましょう。


このパターンを実装するには、まず、**メイン**のログインページをレンダリングする/login
ルートを作成します。


import { Login } from '@/app/ui/login'
export default function Page() {
return <Login />
}
次に、@auth
スロット内に、null
を返すdefault.js
ファイルを追加します。これにより、モーダルがアクティブでないときにモーダルがレンダリングされなくなります。
export default function Default() {
return '...'
}
@auth
スロット内で、/(.)login
フォルダーを更新して/login
ルートをインターセプトします。/(.)login/page.tsx
ファイルに<Modal>
コンポーネントとその子コンポーネントをインポートします。
import { Modal } from '@/app/ui/modal'
import { Login } from '@/app/ui/login'
export default function Page() {
return (
<Modal>
<Login />
</Modal>
)
}
知っておくと良いこと
- ルートをインターセプトするために使用される規則(例:
(.)
)は、ファイルシステムの構造によって異なります。ルートインターセプトの規則を参照してください。<Modal>
の機能とモーダルコンテンツ(<Login>
)を分離することで、モーダル内のコンテンツ(例:フォーム)がサーバーコンポーネントであることを保証できます。クライアントコンポーネントとサーバーコンポーネントのインターリーブの詳細については、を参照してください。
モーダルの開く
これで、Next.jsルーターを利用してモーダルを開閉できます。これにより、モーダルが開いている場合と、前後に移動する場合に、URLが正しく更新されます。
モーダルを開くには、@auth
スロットをプロップとして親レイアウトに渡し、children
プロップと一緒にレンダリングします。
import Link from 'next/link'
export default function Layout({
auth,
children,
}: {
auth: React.ReactNode
children: React.ReactNode
}) {
return (
<>
<nav>
<Link href="/login">Open modal</Link>
</nav>
<div>{auth}</div>
<div>{children}</div>
</>
)
}
ユーザーが<Link>
をクリックすると、/login
ページに移動する代わりにモーダルが開きます。ただし、更新時または最初の読み込み時には、/login
に移動すると、ユーザーはメインのログインページに移動します。
モーダルの閉じる
router.back()
を呼び出すか、Link
コンポーネントを使用してモーダルを閉じることができます。
'use client'
import { useRouter } from 'next/navigation'
export function Modal({ children }: { children: React.ReactNode }) {
const router = useRouter()
return (
<>
<button
onClick={() => {
router.back()
}}
>
Close modal
</button>
<div>{children}</div>
</>
)
}
@auth
スロットをレンダリングする必要がなくなったページから離れるためにLink
コンポーネントを使用する場合、パラレルルートがnull
を返すコンポーネントと一致するようにする必要があります。たとえば、ルートページに戻る場合は、@auth/page.tsx
コンポーネントを作成します。
import Link from 'next/link'
export function Modal({ children }: { children: React.ReactNode }) {
return (
<>
<Link href="/">Close modal</Link>
<div>{children}</div>
</>
)
}
export default function Page() {
return '...'
}
または、他のページ(/foo
、/foo/bar
など)に移動する場合は、キャッチオールスロットを使用できます。
export default function CatchAll() {
return '...'
}
知っておくと良いこと
- アクティブ状態とナビゲーションで説明されている動作のため、
@auth
スロットでキャッチオールルートを使用しています。スロットに一致しなくなったルートへのクライアント側のナビゲーションは表示されたままになるため、モーダルを閉じるには、スロットをnull
を返すルートに一致させる必要があります。- ギャラリーでフォトモーダルを開き、専用の
/photo/[id]
ページも持つこと、またはサイドモーダルでショッピングカートを開くことなどが、他の例として挙げられます。- インターセプトされたルートとパラレルルートを使用したモーダルの例をご覧ください。
読み込み中UIとエラーUI
パラレルルートは個別にストリーミングできるため、各ルートに独立したエラー状態と読み込み状態を定義できます。


役に立ちましたか?