typescriptadvanced

Parallel Routes for Modal Pattern

Implement modal dialogs using parallel routes and route interception for shareable modal URLs.

typescript
// File structure:
// app/
//   @modal/
//     (.)photo/[id]/page.tsx  <- intercepted route (modal)
//     default.tsx
//   photo/[id]/page.tsx       <- full page
//   layout.tsx
//   page.tsx

// app/layout.tsx
export default function Layout({
  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; // No modal by default
}

// app/@modal/(.)photo/[id]/page.tsx
import { Modal } from '@/components/Modal';

export default async function PhotoModal({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;
  const photo = await getPhoto(id);

  return (
    <Modal>
      <img src={photo.url} alt={photo.alt} className="max-h-[80vh]" />
      <p>{photo.caption}</p>
    </Modal>
  );
}

// app/photo/[id]/page.tsx (full page fallback)
export default async function PhotoPage({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;
  const photo = await getPhoto(id);

  return (
    <div className="max-w-4xl mx-auto p-8">
      <img src={photo.url} alt={photo.alt} className="w-full" />
      <h1 className="text-2xl mt-4">{photo.caption}</h1>
    </div>
  );
}

// components/Modal.tsx
'use client';
import { useRouter } from 'next/navigation';

export function Modal({ children }: { children: React.ReactNode }) {
  const router = useRouter();
  return (
    <div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50"
      onClick={() => router.back()}>
      <div className="bg-white rounded-xl p-6 max-w-2xl" onClick={(e) => e.stopPropagation()}>
        {children}
      </div>
    </div>
  );
}

Use Cases

  • photo galleries
  • product quickview
  • shareable modal URLs

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.