Route modals

Render the modal in an @modal slot; close it with router.back().

From next-best-practices, parallel-routes.md.

Closing is a history operation

Open a photo, close it, then press Back.

router.push('/')Bad
History

    Backs to leave 1

    router.back()Good
    History

      Backs to leave 1

      // components/modal.tsx
      'use client'
      import { useRouter } from 'next/navigation'
      
      export function Modal({ children }: { children: React.ReactNode }) {
        const router = useRouter()
        const close = () => router.back()
      
        return (
          <div
            className="overlay"
            onClick={(e) => e.target === e.currentTarget && close()}
            onKeyDown={(e) => e.key === 'Escape' && close()}
          >
            <div role="dialog" aria-modal="true">
              <button onClick={close}>Close</button>
              {children}
            </div>
          </div>
        )
      }

      Reload skips the intercept

      Open a photo, press Refresh, then untick default.tsx.

      BrowserHard load
      What renderedapp/
      • layout.tsx{ children, modal }
      • page.tsxchildren
      • photos/[id]/page.tsxchildren
      • @modal/default.tsxmodal
      • @modal/(.)photos/[id]/page.tsxmodal

      // app/layout.tsx
      export default function RootLayout({ children, modal }: {
        children: React.ReactNode
        modal: React.ReactNode
      }) {
        return (<html><body>{children}{modal}</body></html>)
      }
      
      // app/@modal/default.tsx
      export default function Default() {
        return null
      }

      Matchers count segments

      Pick a slot and a matcher.

        Read as foldersWrong
        You expect it to intercept /photos

        Read as route segmentsRight
        It actually intercepts /photos

        Why it matters, pros and cons

        Why it matters

        Opening was a navigation, so closing must undo it. push reopens it on Back.

        Pros

        • Shareable URL; refresh shows the full page.
        • Back and Forward work as expected.
        • The gallery keeps its scroll underneath.

        Cons

        • Two pages per photo to keep in sync.
        • Landing directly, router.back() leaves your site.
        • A URL-less dialog needs none of this.