Параллельные маршруты (Parallel Routes)
Параллельная маршрутизация позволяет одновременно или условно рендерить одну или несколько страниц в одном лейауте. Для высокодинамичных разделов приложений, таких как дашборды и ленты в соцсетях, параллельная маршрутизация может использоваться для реализации сложных паттернов навигации.
Например, вы можете одновременно рендерить страницы команды и аналитики.

Параллельная маршрутизация позволяет определять независимые состояния ошибок и загрузки для каждого маршрута, так как они загружаются независимо.

Параллельная маршрутизация также позволяет условно рендерить слот на основе определенных условий, таких как состояние аутентификации. Это позволяет полностью разделять код для одного URL.

Конвенция
Параллельные маршруты создаются с использованием именованных слотов. Слоты определяются с помощью соглашения @folder
и передаются в лейаут того же уровня в качестве пропсов.
Слоты не являются сегментами маршрута и не влияют на структуру URL. Путь
/@team/members
будет доступен по адресу/members
.
Например, следующая структура файлов определяет два явных слота: @analytics
и @team
.

Приведенная структура папок означает, что компонент в 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
Вы можете определить файл default.js
для рендеринга в качестве резервного варианта, когда Next.js не может восстановить активное состояние слота на основе текущего URL.
Рассмотрим следующую структуру папок. Слот @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
или /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>
)
}
Чтобы содержимое модального окна не рендерилось, когда оно неактивно, вы можете создать файл default.js
, который возвращает null
.
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>
)
}
Подробнее о модальных окнах рассказывается в разделе Перехватывающие маршруты.
Если вы хотите перейти в другое место и закрыть модальное окно, вы также можете использовать catch-all маршрут.

export default function CatchAll() {
return null
}
export default function CatchAll() {
return null
}
Catch-all маршруты имеют приоритет над
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
}

Обработка ошибок
Обрабатывайте ошибки времени выполнения, автоматически оборачивая сегменты маршрутов и их вложенные дочерние элементы в React Error Boundary.
Перехватывающие маршруты (Intercepting Routes)
Используйте перехватывающие маршруты для загрузки нового маршрута в текущем макете с маскировкой URL в браузере, что полезно для сложных паттернов маршрутизации, таких как модальные окна.