パラレルルーティング (Parallel Routes)
パラレルルーティング (Parallel Routing) を使用すると、同一レイアウト内で複数のページを同時または条件付きでレンダリングできます。ダッシュボードやソーシャルサイトのフィードなど、アプリケーションの高度に動的なセクションにおいて、複雑なルーティングパターンを実装する際に有用です。
例えば、チームページと分析ページを同時にレンダリングできます。

パラレルルーティングでは、各ルートが独立してストリーミングされる際に、個別のエラー状態やローディング状態を定義できます。

また、認証状態などの条件に基づいてスロットを条件付きでレンダリングすることも可能です。これにより、同一URL上で完全に分離されたコードを実現できます。

規約
パラレルルートは名前付きスロットを使用して作成されます。スロットは @folder
規約で定義され、同じ階層のレイアウトにプロパティとして渡されます。
スロットはルートセグメントではなく、URL構造に影響しません。
/@team/members
というファイルパスは/members
でアクセス可能です。
例えば、以下のファイル構造では @analytics
と @team
の2つの明示的なスロットが定義されています。

上記のフォルダ構造により、app/layout.js
のコンポーネントは @analytics
と @team
のスロットプロパティを受け取り、children
プロパティと並行してレンダリングできます:
export default function Layout(props: {
children: React.ReactNode
analytics: React.ReactNode
team: React.ReactNode
}) {
return (
<>
{props.children}
{props.team}
{props.analytics}
</>
)
}
export default function Layout(props) {
return (
<>
{props.children}
{props.team}
{props.analytics}
</>
)
}
補足:
children
プロパティはフォルダにマップする必要のない暗黙的なスロットです。つまりapp/page.js
はapp/@children/page.js
と同等です。
不一致ルート
デフォルトでは、スロット内にレンダリングされるコンテンツは現在のURLと一致します。
スロットが一致しない場合、Next.jsがレンダリングするコンテンツはルーティング技術とフォルダ構造によって異なります。
default.js
Next.jsが現在のURLに基づいてスロットのアクティブ状態を回復できない場合、フォールバックとして default.js
ファイルを定義できます。
以下のフォルダ構造を考えてみましょう。@team
スロットには settings
ディレクトリがありますが、@analytics
にはありません。

ナビゲーション時
ナビゲーション時、Next.jsは現在のURLと一致しなくても、スロットの以前のアクティブ状態をレンダリングします。
リロード時
リロード時、Next.jsはまず不一致スロットの default.js
ファイルをレンダリングしようとします。利用できない場合、404がレンダリングされます。
不一致ルートに対する404は、意図せず並行レンダリングすべきでないルートをレンダリングしないようにするために役立ちます。
useSelectedLayoutSegment(s)
useSelectedLayoutSegment
と useSelectedLayoutSegments
はどちらも parallelRoutesKey
を受け取り、そのスロット内のアクティブルートセグメントを読み取ることができます。
'use client'
import { useSelectedLayoutSegment } from 'next/navigation'
export default async function Layout(props: {
//...
auth: React.ReactNode
}) {
const loginSegments = useSelectedLayoutSegment('auth')
// ...
}
'use client'
import { useSelectedLayoutSegment } from 'next/navigation'
export default async function Layout(props) {
const loginSegments = useSelectedLayoutSegment('auth')
// ...
}
ユーザーが @auth/login
またはURLバーで /login
にナビゲートすると、loginSegments
は文字列 "login"
と等しくなります。
例
モーダル
パラレルルーティングを使用してモーダルをレンダリングできます。

@auth
スロットは <Modal>
コンポーネントをレンダリングし、/login
などの一致するルートにナビゲートすることで表示できます。
export default async function Layout(props: {
// ...
auth: React.ReactNode
}) {
return (
<>
{/* ... */}
{props.auth}
</>
)
}
export default async function Layout(props) {
return (
<>
{/* ... */}
{props.auth}
</>
)
}
import { Modal } from 'components/modal'
export default function Login() {
return (
<Modal>
<h1>ログイン</h1>
{/* ... */}
</Modal>
)
}
import { Modal } from 'components/modal'
export default function Login() {
return (
<Modal>
<h1>ログイン</h1>
{/* ... */}
</Modal>
)
}
モーダルがアクティブでないときにコンテンツがレンダリングされないようにするには、null
を返す default.js
ファイルを作成します。
export default function Default() {
return null
}
export default function Default() {
return null
}
モーダルの閉じ方
<Link href="/login">
などのクライアントナビゲーションを通じてモーダルが開始された場合、router.back()
を呼び出すか Link
コンポーネントを使用してモーダルを閉じることができます。
'use client'
import { useRouter } from 'next/navigation'
import { Modal } from 'components/modal'
export default async function Login() {
const router = useRouter()
return (
<Modal>
<span onClick={() => router.back()}>モーダルを閉じる</span>
<h1>ログイン</h1>
...
</Modal>
)
}
'use client'
import { useRouter } from 'next/navigation'
import { Modal } from 'components/modal'
export default async function Login() {
const router = useRouter()
return (
<Modal>
<span onClick={() => router.back()}>モーダルを閉じる</span>
<h1>ログイン</h1>
...
</Modal>
)
}
モーダルに関する詳細は インターセプティングルート セクションで説明されています。
他の場所にナビゲートしてモーダルを閉じたい場合は、キャッチオールルートも使用できます。

export default function CatchAll() {
return null
}
export default function CatchAll() {
return null
}
キャッチオールルートは
default.js
よりも優先されます。
条件付きルーティング
パラレルルーティングを使用して条件付きルーティングを実装できます。例えば、認証状態に応じて @dashboard
または @login
ルートをレンダリングできます。
import { getUser } from '@/lib/auth'
export default function Layout({
dashboard,
login,
}: {
dashboard: React.ReactNode
login: React.ReactNode
}) {
const isLoggedIn = getUser()
return isLoggedIn ? dashboard : login
}
import { getUser } from '@/lib/auth'
export default function Layout({ dashboard, login }) {
const isLoggedIn = getUser()
return isLoggedIn ? dashboard : login
}
