Route intercettanti per esperienze modali condivise
Usi le convenzioni (.) e (..) per mostrare i contenuti in una modale mantenendo un URL condivisibile.
Route intercettanti per esperienze modali condivise è una lezione Next.js 15 Fullstack (App Router + Server Actions) gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Next.js 15 Fullstack (App Router + Server Actions), e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Next.js 15 Fullstack (App Router + Server Actions) include 4 lezioni in totale.
Che cosa sono le route intercettanti?
Next.js 15 introduce le route intercettanti: una convenzione di routing che consente di caricare una route nel contesto del layout corrente, invece di passare a una pagina completamente nuova.
Il caso d'uso più comune è il pattern della modale: facendo clic su una foto in una griglia, la foto viene mostrata in una sovrapposizione modale, ma l'URL viene aggiornato a /photos/42. Se condivide quell'URL, il destinatario visualizza la pagina completa della foto, non la modale.
- L'URL del browser cambia, quindi può essere condiviso e aggiunto ai segnalibri
- La pagina corrente rimane visibile dietro la modale
- Un aggiornamento completo della pagina o una navigazione diretta carica la pagina completa indipendente
Questo pattern viene talvolta chiamato pattern della navigazione soft con modale ed è stato reso popolare da Pinterest, Instagram e dallo stesso sito di Vercel.
Le convenzioni di intercettazione: (.) e (..)
Next.js usa prefissi nei nomi delle cartelle per dichiarare che una route intercetta un'altra. Il prefisso rispecchia la sintassi dei percorsi relativi:
(.)— intercetta una route allo stesso livello nell'albero dei segmenti(..)— intercetta una route un livello più in alto(...)— intercetta una route dalla root dell'app
La cartella denominata con questi prefissi si trova all'interno di uno slot di route parallelo (una cartella con prefisso @), perciò le due funzionalità lavorano insieme.
Esempio: per intercettare /photos/[id] dalla home page /, crei app/@modal/(.)photos/[id]/page.tsx.
Struttura del progetto per la modale delle foto
Costruiamo un esempio concreto: una galleria di foto in cui facendo clic su una miniatura si apre una modale, mentre l'URL diventa /photos/42.
L'albero dei file è il seguente:
app/
layout.tsx ← root layout with @modal slot
page.tsx ← gallery grid (home)
@modal/
default.tsx ← renders null (slot default)
(.)photos/
[id]/
page.tsx ← intercepted modal UI
photos/
[id]/
page.tsx ← full standalone photo page
Quando l'utente fa clic su una foto dalla galleria, Next.js visualizza @modal/(.)photos/[id]/page.tsx all'interno dello slot @modal del root layout. Un aggiornamento completo di /photos/42 visualizza normalmente photos/[id]/page.tsx.
Root layout: accettare lo slot @modal
Il root layout deve accettare lo slot parallelo @modal come prop insieme a children. È così che Next.js inserisce il contenuto della route intercettata.
// app/layout.tsx
import type { ReactNode } from 'react';
interface RootLayoutProps {
children: ReactNode;
modal: ReactNode; // injected by the @modal parallel route slot
}
export default function RootLayout({ children, modal }: RootLayoutProps) {
return (
<html lang="en">
<body>
{children}
{modal} {/* renders null by default, modal content when intercepted */}
</body>
</html>
);
}Slot predefinito: non visualizzare nulla quando nessuna modale è attiva
Quando l'utente visita / senza alcuna intercettazione, Next.js deve visualizzare qualcosa per lo slot @modal. A questo scopo si fornisce un default.tsx che restituisce null.
Senza questo file, Next.js restituisce un 404 perché non riesce a risolvere lo slot.
// app/@modal/default.tsx
// This file is required — it tells Next.js to render nothing
// when no route is being intercepted into this slot.
export default function ModalDefault() {
return null;
}La pagina della route intercettante: interfaccia della modale
Ora crei la pagina della route intercettata. È ciò che viene visualizzato nello slot @modal quando l'utente esegue una navigazione soft (tramite <Link>) verso /photos/[id].
La pagina riceve gli stessi params della pagina reale e può recuperare gli stessi dati. Il contenitore della modale è semplicemente un componente: è lei a controllare lo stile della sovrapposizione.
// app/@modal/(.)photos/[id]/page.tsx
import { Modal } from '@/components/Modal';
import { getPhoto } from '@/lib/photos';
interface Props {
params: Promise<{ id: string }>;
}
export default async function PhotoModal({ params }: Props) {
const { id } = await params;
const photo = await getPhoto(id);
return (
<Modal>
<img
src={photo.url}
alt={photo.title}
className="max-w-full max-h-screen object-contain"
/>
<h2 className="mt-4 text-lg font-semibold">{photo.title}</h2>
</Modal>
);
}Creare il componente Modale con router.back()
Il componente Modal deve chiudersi quando l'utente lo nasconde. Il modo idiomatico consiste nell'usare router.back(): questo torna indietro nella cronologia, rimuove la route intercettata e ripristina la pagina della galleria senza un ricaricamento completo.
Usi una direttiva 'use client' perché useRouter è un hook lato client.
// components/Modal.tsx
'use client';
import { useRouter } from 'next/navigation';
import { useEffect, useCallback, type ReactNode } from 'react';
export function Modal({ children }: { children: ReactNode }) {
const router = useRouter();
const close = useCallback(() => router.back(), [router]);
// Close on Escape key
useEffect(() => {
const handler = (e: KeyboardEvent) => {
if (e.key === 'Escape') close();
};
window.addEventListener('keydown', handler);
return () => window.removeEventListener('keydown', handler);
}, [close]);
return (
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black/70"
onClick={close} // click backdrop to close
>
<div
className="relative bg-white rounded-xl p-6 max-w-2xl w-full"
onClick={(e) => e.stopPropagation()} // prevent backdrop close on content
>
<button
onClick={close}
className="absolute top-3 right-3 text-gray-500 hover:text-gray-900"
aria-label="Close modal"
>
✕
</button>
{children}
</div>
</div>
);
}La pagina completa della foto: fallback per la navigazione diretta
La pagina indipendente /photos/[id] è quella che gli utenti visualizzano quando visitano direttamente l'URL, ad esempio tramite un link condiviso, un aggiornamento completo della pagina o un crawler di un motore di ricerca. Visualizza il layout completo della pagina senza alcun contenitore modale.
Questa pagina e quella intercettata possono condividere la stessa funzione di recupero dei dati (getPhoto): non è necessaria alcuna duplicazione per il livello di accesso ai dati.
// app/photos/[id]/page.tsx
import { getPhoto } from '@/lib/photos';
import Link from 'next/link';
interface Props {
params: Promise<{ id: string }>;
}
export default async function PhotoPage({ params }: Props) {
const { id } = await params;
const photo = await getPhoto(id);
return (
<main className="container mx-auto py-12">
<Link href="/" className="text-blue-600 hover:underline mb-6 inline-block">
← Back to gallery
</Link>
<img
src={photo.url}
alt={photo.title}
className="w-full rounded-xl shadow-lg"
/>
<h1 className="mt-6 text-3xl font-bold">{photo.title}</h1>
<p className="mt-2 text-gray-600">{photo.description}</p>
</main>
);
}La pagina della galleria: collegarsi alla modale
La pagina iniziale della galleria si collega a /photos/[id] usando un componente <Link> standard. Next.js rileva automaticamente la route intercettante: non è necessaria alcuna prop speciale sul link.
Quando JavaScript è abilitato e l'utente fa clic sul link, l'intercettazione viene eseguita. Senza JavaScript (o al primo caricamento), la pagina completa viene caricata normalmente.
// app/page.tsx
import Link from 'next/link';
import { getPhotos } from '@/lib/photos';
export default async function GalleryPage() {
const photos = await getPhotos();
return (
<main className="container mx-auto py-12">
<h1 className="text-3xl font-bold mb-8">Photo Gallery</h1>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{photos.map((photo) => (
<Link key={photo.id} href={`/photos/${photo.id}`}>
<img
src={photo.thumbnailUrl}
alt={photo.title}
className="rounded-lg aspect-square object-cover hover:opacity-90 transition"
/>
</Link>
))}
</div>
</main>
);
}Usare (..) per intercettare un livello più in alto
Il prefisso (..) intercetta una route che si trova un segmento più in alto nell'albero degli URL rispetto alla posizione dello slot.
Consideri una pagina di prodotto all'indirizzo /shop/[category]/[productId]. Desidera che facendo clic su un prodotto all'interno di /shop/[category] si apra una modale. Lo slot @modal si trova all'interno di app/shop/[category]/, ma la route di destinazione è anch'essa un livello più in basso; quindi usa (..) per risalire di un livello e intercettarla da lì.
Esempio di percorso del file:
app/shop/[category]/
@modal/
default.tsx
(..)shop/[category]/[productId]/
page.tsx ← intercepts the product page
La regola generale è contare quanti livelli di segmenti separano la cartella @modal dalla route di destinazione e usare altrettanti punti.
Interfaccia di caricamento e Suspense nelle route intercettate
Le pagine delle route intercettate sono Server Components asincroni, quindi è possibile racchiuderle in Suspense per mostrare uno skeleton durante il recupero dei dati, esattamente come per qualsiasi altra route Next.js.
Aggiunga un file loading.tsx accanto alla pagina intercettata oppure racchiuda il contenuto in <Suspense fallback={...}> direttamente nel componente della modale.
// app/@modal/(.)photos/[id]/loading.tsx
// Shown while the async PhotoModal page is fetching data.
export default function PhotoModalSkeleton() {
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/70">
<div className="bg-white rounded-xl p-6 max-w-2xl w-full animate-pulse">
<div className="h-80 bg-gray-200 rounded-lg" />
<div className="mt-4 h-6 bg-gray-200 rounded w-1/2" />
</div>
</div>
);
}Verifica delle conoscenze: quale convenzione usare?
Verifichi la sua comprensione delle convenzioni delle route intercettanti.
Riepilogo della lezione: route intercettanti per esperienze con modali
In questa lezione ha imparato a implementare il pattern della modale che mantiene l'URL in Next.js 15 usando le route intercettanti.
- La convenzione (.) intercetta una route allo stesso livello di segmenti; (..) risale di un livello; (...) raggiunge la root dell'app.
- Le route intercettanti si trovano sempre all'interno di uno slot di route parallelo (
@modal), così il layout padre può visualizzare contemporaneamente sia la pagina di sfondo sia la modale. - Nello slot è necessario un default.tsx che restituisca
nullper evitare errori 404 quando nessuna modale è attiva. - La modale si chiude con
router.back(), che rimuove la route intercettata dalla cronologia senza un ricaricamento completo. - La pagina completa indipendente al percorso reale della route continua a esistere: gli aggiornamenti completi e i link condivisi caricano sempre la pagina completa, rendendo l'URL realmente condivisibile.
- Aggiunga
loading.tsxo Suspense all'interno della route intercettata per mostrare uno skeleton durante il caricamento dei dati asincroni.
Questo pattern offre agli utenti un'esperienza rapida simile a quella di un'app, mantenendo al contempo le pagine ottimizzate per la SEO e accessibili a chiunque disponga semplicemente di un link.
Impara TypeScript con un tutor IA — gratis
Scrivi ed esegui vero codice nel tuo browser, ricevi aiuto istantaneo da un tutor IA disponibile 24/7, e riprendi da dove hai lasciato sul web o nell'app.
- Corsi
- 22
- Lezioni
- 88
Domande Frequenti
La lezione «Route intercettanti per esperienze modali condivise» è gratuita?
Sì — il testo completo di «Route intercettanti per esperienze modali condivise» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Next.js 15 Fullstack (App Router + Server Actions), passa a CoddyKit PRO. Il corso Next.js 15 Fullstack (App Router + Server Actions) include 4 lezioni in totale.
Cosa imparerò in «Route intercettanti per esperienze modali condivise»?
Usi le convenzioni (.) e (..) per mostrare i contenuti in una modale mantenendo un URL condivisibile. Eserciti Next.js 15 Fullstack (App Router + Server Actions) con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare Next.js 15 Fullstack (App Router + Server Actions)?
Non è richiesta alcuna esperienza precedente. Next.js 15 Fullstack (App Router + Server Actions) su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.
Quanto tempo richiede la lezione «Route intercettanti per esperienze modali condivise»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione Next.js 15 Fullstack (App Router + Server Actions)?
Sì. Ogni lezione Next.js 15 Fullstack (App Router + Server Actions) include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Route parallele con slot nominati e segmenti predefiniti
- Route intercettanti per esperienze modali condivise
- Rendering condizionale degli slot per dashboard e tab
- Creazione di un flusso foto-modale con navigazione morbida