diff --git a/docs/02-app/01-building-your-application/01-routing/10-parallel-routes.mdx b/docs/02-app/01-building-your-application/01-routing/10-parallel-routes.mdx
index 91261c114031c..0bffa7457777f 100644
--- a/docs/02-app/01-building-your-application/01-routing/10-parallel-routes.mdx
+++ b/docs/02-app/01-building-your-application/01-routing/10-parallel-routes.mdx
@@ -15,7 +15,7 @@ For example, considering a dashboard, you can use parallel routes to simultaneou
srcLight="/docs/light/parallel-routes.png"
srcDark="/docs/dark/parallel-routes.png"
width="1600"
- height="952"
+ height="942"
/>
## Slots
@@ -33,28 +33,32 @@ Parallel routes are created using named **slots**. Slots are defined with the `@
Slots are passed as props to the shared parent layout. For the example above, the component in `app/layout.js` now accepts the `@analytics` and `@team` slots props, and can render them in parallel alongside the `children` prop:
```tsx filename="app/layout.tsx" switcher
-export default function Layout(props: {
+export default function Layout({
+ children,
+ team,
+ analytics,
+}: {
children: React.ReactNode
analytics: React.ReactNode
team: React.ReactNode
}) {
return (
<>
- {props.children}
- {props.team}
- {props.analytics}
+ {children}
+ {team}
+ {analytics}
>
)
}
```
```jsx filename="app/layout.js" switcher
-export default function Layout(props) {
+export default function Layout({ children, team, analytics }) {
return (
<>
- {props.children}
- {props.team}
- {props.analytics}
+ {children}
+ {team}
+ {analytics}
>
)
}
@@ -71,17 +75,17 @@ However, slots are **not** [route segments](/docs/app/building-your-application/
By default, Next.js keeps track of the active _state_ (or subpage) for each slot. However, the content rendered within a slot will depend on the type of navigation:
- [**Soft Navigation**](/docs/app/building-your-application/routing/linking-and-navigating#5-soft-navigation): During client-side navigation, Next.js will perform a [partial render](/docs/app/building-your-application/routing/linking-and-navigating#4-partial-rendering), changing the subpage within the slot, while maintaining the other slot's active subpages, even if they don't match the current URL.
-- **Hard Navigation**: After a full-page load (browser refresh), Next.js cannot determine the active state of slots that don't match the current URL. Instead, it will render a [`default.js`](#defaultjs) file for the unmatched slots, or `404` if `default.js` doesn't exist.
+- **Hard Navigation**: After a full-page load (browser refresh), Next.js cannot determine the active state for the slots that don't match the current URL. Instead, it will render a [`default.js`](#defaultjs) file for the unmatched slots, or `404` if `default.js` doesn't exist.
> **Good to know**:
>
-> - The `404` for unmatched routes helps ensure that you don't accidentally render a route that shouldn't be parallel rendered.
+> - The `404` for unmatched routes helps ensure that you don't accidentally render a parallel route on a page that it was not intended for.
### `default.js`
You can define a `default.js` file to render as a fallback for unmatched slots during the initial load or full-page reload.
-Consider the following folder structure. The `@team` slot has a `settings` page, but `@analytics` does not.
+Consider the following folder structure. The `@team` slot has a `/settings` page, but `@analytics` does not.
-When navigating to `/dashboard/settings`, the `@team` slot will render the `settings` page while maintaining the currently active page for the `@analytics` slot.
+When navigating to `/dashboard/settings`, the `@team` slot will render the `/settings` page while maintaining the currently active page for the `@analytics` slot.
On refresh, Next.js will render a `default.js` for `@analytics`. If `default.js` doesn't exist, a `404` is rendered instead.
@@ -106,10 +110,7 @@ Both [`useSelectedLayoutSegment`](/docs/app/api-reference/functions/use-selected
import { useSelectedLayoutSegment } from 'next/navigation'
-export default function Layout(props: {
- //...
- auth: React.ReactNode
-}) {
+export default function Layout({ auth }: { auth: React.ReactNode }) {
const loginSegments = useSelectedLayoutSegment('auth')
// ...
}
@@ -120,7 +121,7 @@ export default function Layout(props: {
import { useSelectedLayoutSegment } from 'next/navigation'
-export default function Layout(props) {
+export default function Layout({ auth }) {
const loginSegments = useSelectedLayoutSegment('auth')
// ...
}
@@ -130,72 +131,136 @@ When a user navigates to `app/@auth/login` (or `/login` in the URL bar), `loginS
## Examples
-### Modals
+### Conditional Routes
-Parallel Routing can be used to render modals.
+You can use Parallel Routes to conditionally render routes based on certain conditions, such as user role. For example, to render a different dashboard page for the `/admin` or `/user` roles:
-The `@auth` slot renders a `` component that can be shown by navigating to a matching route, for example `/login`.
+```tsx filename="app/dashboard/layout.tsx" switcher
+import { checkUserRole } from '@/lib/auth'
-```tsx filename="app/layout.tsx" switcher
-export default async function Layout(props: {
- // ...
- auth: React.ReactNode
+export default function Layout({
+ user,
+ admin,
+}: {
+ user: React.ReactNode
+ admin: React.ReactNode
}) {
+ const role = checkUserRole()
+ return <>{role === 'admin' ? admin : user}>
+}
+```
+
+```jsx filename="app/dashboard/layout.js" switcher
+import { checkUserRole } from '@/lib/auth'
+
+export default function Layout({ user, admin }) {
+ const role = checkUserRole()
+ return <>{role === 'admin' ? admin : user}>
+}
+```
+
+## Tab Groups
+
+You can add a `layout` inside a slot to allow users to navigate the slot independently. This is useful for creating tabs.
+
+For example, the `@analytics` slot has two subpages: `/page-views` and `/visitors`.
+
+
+
+Within `@analytics`, create a [`layout`](/docs/app/building-your-application/routing/pages-and-layouts) file to share the tabs between the two pages:
+
+```tsx filename="app/dashboard/@analytics/layout.tsx" switcher
+import Link from 'next/link'
+
+export default function Layout({ children }: { children: React.ReactNode }) {
return (
<>
- {/* ... */}
- {props.auth}
+
+
{children}
>
)
}
```
```jsx filename="app/layout.js" switcher
-export default async function Layout(props) {
+import Link from 'next/link'
+
+export default function Layout({ children }: { children: React.ReactNode }) {
return (
<>
- {/* ... */}
- {props.auth}
+
+
- {/* ... */}
-
- )
+Parallel Routes can be used together with [Intercepting Routes](/docs/app/building-your-application/routing/intercepting-routes) to create modals. This allows you to solve common challenges when building modals, such as:
+
+- Making the modal content **shareable through a URL**.
+- **Preserving context** when the page is refreshed, instead of closing the modal.
+- **Closing the modal on backwards navigation** rather than going to the previous route.
+- **Reopening the modal on forwards navigation**.
+
+Consider the following UI pattern, where a user can open a login modal from a layout using client-side navigation, or access a separate `/login` page:
+
+
+
+To implement this pattern, start by creating a `/login` route that renders your **main** login page.
+
+
+
+```tsx filename="app/login/page.tsx" switcher
+import { Login } from '@/app/ui/login'
+
+export default function Page() {
+ return
}
```
-```jsx filename="app/@auth/login/page.js" switcher
-import { Modal } from 'components/modal'
+```jsx filename="app/login/page.js" switcher
+import { Login } from '@/app/ui/login'
-export default function Login() {
- return (
-
-
Login
- {/* ... */}
-
- )
+export default function Page() {
+ return
}
```
-To ensure that the contents of the modal don't get rendered when it's not active, you can create a `default.js` file that returns `null`.
+Then, inside the `@auth` slot, add [`default.js`](/docs/app/api-reference/file-conventions/default) file that returns `null`. This ensures that the modal is not rendered when it's not active.
```tsx filename="app/@auth/default.tsx" switcher
export default function Default() {
@@ -209,117 +274,142 @@ export default function Default() {
}
```
-#### Dismissing a modal
-
-If a modal was initiated through client navigation, e.g. by using ``, you can dismiss the modal by calling `router.back()` or by using a `Link` component.
+Inside your `@auth` slot, intercept the `/login` route by updating the `/(.)login` folder. Import the `` component and its children into the `/(.)login/page.tsx` file:
-```tsx filename="app/@auth/login/page.tsx" highlight="5" switcher
-'use client'
-import { useRouter } from 'next/navigation'
-import { Modal } from 'components/modal'
+```tsx filename="app/@auth/(.)login/page.tsx" switcher
+import { Modal } from '@/app/ui/modal'
+import { Login } from '@/app/ui/login'
-export default function Login() {
- const router = useRouter()
+export default function Page() {
return (
- router.back()}>Close modal
-
Login
- ...
+
)
}
```
-```jsx filename="app/@auth/login/page.js" highlight="5" switcher
-'use client'
-import { useRouter } from 'next/navigation'
-import { Modal } from 'components/modal'
+```jsx filename="app/@auth/(.)login/page.js" switcher
+import { Modal } from '@/app/ui/modal'
+import { Login } from '@/app/ui/login'
-export default function Login() {
- const router = useRouter()
+export default function Page() {
return (
- router.back()}>Close modal
-
Login
- ...
+
)
}
```
-> More information on modals is covered in the [Intercepting Routes](/docs/app/building-your-application/routing/intercepting-routes) section.
+> **Good to know:**
+>
+> - The convention used to intercept the route, e.g. `(.)`, depends on your file-system structure. See [Intercepting Routes convention](/docs/app/building-your-application/routing/intercepting-routes#convention).
+> - By separating the `` functionality from the modal content (``), you can ensure any content inside the modal, e.g. [forms](/docs/app/building-your-application/data-fetching/server-actions-and-mutations#forms), are Server Components. See [Interleaving Client and Server Components](/docs/app/building-your-application/rendering/composition-patterns#supported-pattern-passing-server-components-to-client-components-as-props) for more information.
-If you want to navigate elsewhere and dismiss a modal, you can also use a catch-all route.
+### Opening the modal
-
+Now, you can leverage the Next.js router to open and close the modal. This ensures the URL is correctly updated when the modal is open, and when navigating backwards and forwards.
-```tsx filename="app/@auth/[...catchAll]/page.tsx" switcher
-export default function CatchAll() {
- return null
+To open the modal, pass the `@auth` slot as a prop to the parent layout and render it alongside the `children` prop.
+
+```tsx filename="app/layout.tsx" switcher
+import Link from 'next/link'
+
+export default function Layout({
+ auth,
+ children,
+}: {
+ auth: React.ReactNode
+ children: React.ReactNode
+}) {
+ return (
+ <>
+
+
{auth}
+
{children}
+ >
+ )
}
```
-```jsx filename="app/@auth/[...catchAll]/page.js" switcher
-export default function CatchAll() {
- return null
+```jsx filename="app/layout.js" switcher
+import Link from 'next/link'
+
+export default function Layout({ auth, children }) {
+ return (
+ <>
+
+
{auth}
+
{children}
+ >
+ )
}
```
-> Catch-all routes take precedence over `default.js`.
+When the user clicks the ``, the modal will open instead of navigating to the `/login` page. However, on refresh or initial load, navigating to `/login` will take the user to the main login page.
-### Conditional Routes
+### Closing the modal
-Parallel Routes also allows you to conditionally render a slot based on certain conditions, such as authentication state. For example, you can render a `/dashboard` or `/login` page depending on whether the user is logged in:
+You can close the modal by calling `router.back()` or by using the `Link` component.
-
+```tsx filename="app/ui/modal.tsx switcher
+'use client'
-Parallel Routes can be used to implement conditional routing. For example, you can render a `@dashboard` or `@login` route depending on the authentication state.
+import { useRouter } from 'next/navigation'
-```tsx filename="app/layout.tsx" switcher
-import { getUser } from '@/lib/auth'
+export function Modal({ children }: { children: React.ReactNode }) {
+ const router = useRouter()
-export default function Layout({
- dashboard,
- login,
-}: {
- dashboard: React.ReactNode
- login: React.ReactNode
-}) {
- const isLoggedIn = getUser()
- return isLoggedIn ? dashboard : login
+ return (
+ <>
+
+
+ >
+ )
}
```
-
+> **Good to know:**
+>
+> - Other examples could include opening a photo modal in a gallery while also having a dedicated `/photo/[id]` page, or opening a shopping cart in a side modal.
+> - [View an example](https://github.com/vercel-labs/nextgram) of modals with Intercepted and Parallel Routes.
-### Streaming
+### Loading and Error UI
Parallel Routes can be streamed independently, allowing you to define independent error and loading states for each route:
@@ -330,3 +420,5 @@ Parallel Routes can be streamed independently, allowing you to define independen
width="1600"
height="1218"
/>
+
+See the [Loading UI](/docs/app/building-your-application/routing/loading-ui-and-streaming) and [Error Handling](/docs/app/building-your-application/routing/error-handling) documentation for more information.
diff --git a/docs/02-app/01-building-your-application/01-routing/11-intercepting-routes.mdx b/docs/02-app/01-building-your-application/01-routing/11-intercepting-routes.mdx
index 1895e0105f93f..180e970c828be 100644
--- a/docs/02-app/01-building-your-application/01-routing/11-intercepting-routes.mdx
+++ b/docs/02-app/01-building-your-application/01-routing/11-intercepting-routes.mdx
@@ -57,14 +57,14 @@ For example, you can intercept the `photo` segment from within the `feed` segmen
### Modals
-Intercepting Routes can be used together with [Parallel Routes](/docs/app/building-your-application/routing/parallel-routes) to create modals.
+Intercepting Routes can be used together with [Parallel Routes](/docs/app/building-your-application/routing/parallel-routes) to create modals. This allows you to solve common challenges when building modals, such as:
-Using this pattern to create modals overcomes some common challenges when working with modals, by allowing you to:
+- Making the modal content **shareable through a URL**.
+- **Preserving context** when the page is refreshed, instead of closing the modal.
+- **Closing the modal on backwards navigation** rather than going to the previous route.
+- **Reopening the modal on forwards navigation**.
-- Make the modal content **shareable through a URL**
-- **Preserve context** when the page is refreshed, instead of closing the modal
-- **Close the modal on backwards navigation** rather than going to the previous route
-- **Reopen the modal on forwards navigation**
+Consider the following UI pattern, where a user can open a photo modal from a gallery using client-side navigation, or navigate to the photo page directly from a shareable URL:
-> In the above example, the path to the `photo` segment can use the `(..)` matcher since `@modal` is a _slot_ and not a _segment_. This means that the `photo` route is only one _segment_ level higher, despite being two _file-system_ levels higher.
+In the above example, the path to the `photo` segment can use the `(..)` matcher since `@modal` is a _slot_ and not a _segment_. This means that the `photo` route is only one _segment_ level higher, despite being two _file-system_ levels higher.
-Other examples could include opening a login modal in a top navbar while also having a dedicated `/login` page, or opening a shopping cart in a side modal.
+See the [Parallel Routes](/docs/app/building-your-application/routing/parallel-routes#modals) documentation for a step-by-step example, or see our [image gallery example](https://github.com/vercel-labs/nextgram).
-[View an example](https://github.com/vercel-labs/nextgram) of modals with Intercepted and Parallel Routes.
+> **Good to know:**
+>
+> - Other examples could include opening a login modal in a top navbar while also having a dedicated `/login` page, or opening a shopping cart in a side modal.