Reittien sieppaus jaetuille modaalinäkymille
Käyttäkää (.)- ja (..)-käytäntöjä sisällön näyttämiseen modaalina säilyttäen jaettavan URL-osoitteen.
Reittien sieppaus jaetuille modaalinäkymille on ilmainen Next.js 15 -fullstack-kehitys (App Router + Server Actions)-oppitunti CoddyKitissä. Tämä on oppitunti 2/4. Voit lukea tästä oppimispolusta kokonaan mitkä tahansa 3 oppituntia ilmaiseksi — sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä käytännön harjoittelun sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Oppitunti kuuluu Next.js 15 -fullstack-kehitys (App Router + Server Actions)-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Next.js 15 -fullstack-kehitys (App Router + Server Actions)-kurssilla on yhteensä 4 oppituntia.
Mitä ovat sieppaavat reitit?
Next.js 15 esittelee sieppaavat reitit — reitityskäytännön, jonka avulla reitti voidaan ladata nykyisen layoutin kontekstissa sen sijaan, että siirryttäisiin kokonaan uudelle sivulle.
Yleisin käyttötapaus on modaalimalli: kun ruudukossa olevaa kuvaa napsautetaan, se näytetään modaalisena peittokuvana, mutta URL-osoite päivittyy muotoon /photos/42. Jos URL-osoite jaetaan, vastaanottaja näkee koko kuvasivun, ei modaalia.
- Selaimen URL-osoite muuttuu (sen voi jakaa ja tallentaa kirjanmerkiksi)
- Nykyinen sivu pysyy näkyvissä modaalin taustalla
- Selainpäivitys tai suora navigointi lataa kokonaisen itsenäisen sivun
Tätä kutsutaan joskus modaalin pehmeän navigoinnin malliksi, jonka Pinterest, Instagram ja Vercelin oma sivusto ovat tehneet tunnetuksi.
Sieppauskäytännöt: (.) ja (..)
Next.js käyttää kansioiden nimien etuliitteitä ilmoittamaan, että yksi reitti sieppaa toisen. Etuliite vastaa suhteellisen polun syntaksia:
(.)— sieppaa reitin segmenttipuun samalta tasolta(..)— sieppaa reitin yhden tason ylempää(...)— sieppaa reitin app-juuresta
Näillä etuliitteillä nimetty kansio sijaitsee rinnakkaisessa reittislotissa (kansiossa, jonka etuliitteenä on @), joten ominaisuudet toimivat yhdessä.
Esimerkki: siepataksenne reitin /photos/[id] kotisivulta / luotte tiedoston app/@modal/(.)photos/[id]/page.tsx.
Kuvamodaalin projektirakenne
Rakennetaan konkreettinen esimerkki: kuvagalleria, jossa pikkukuvan napsauttaminen avaa modaalin, mutta URL-osoitteeksi tulee /photos/42.
Tiedostopuu näyttää tältä:
app/\n layout.tsx ← root layout with @modal slot\n page.tsx ← gallery grid (home)\n @modal/\n default.tsx ← renders null (slot default)\n (.)photos/\n [id]/\n page.tsx ← intercepted modal UI\n photos/\n [id]/\n page.tsx ← full standalone photo page\nKun käyttäjä napsauttaa kuvaa galleriassa, Next.js hahmontaa kohteen @modal/(.)photos/[id]/page.tsx juurilayoutin @modal-slotin sisällä. Osoitteen /photos/42 selainpäivitys hahmontaa kohteen photos/[id]/page.tsx normaalisti.
Juurilayout: @modal-slotin vastaanottaminen
Juurilayoutin on vastaanotettava rinnakkainen @modal-slot propsina children-propin rinnalla. Näin Next.js välittää siepatun reittisisällön layoutille.
// 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>
);
}Oletusslotti: ei mitään hahmonnettavaa, kun modaali ei ole aktiivinen
Kun käyttäjä siirtyy osoitteeseen / ilman sieppausta, Next.js:n on hahmonnettava jotakin @modal-slotille. Tätä varten toimitatte default.tsx-tiedoston, joka palauttaa null-arvon.
Ilman tätä tiedostoa Next.js palauttaa 404-virheen, koska se ei pysty ratkaisemaan slotia.
// 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;
}Sieppaavan reitin sivu: modaalin käyttöliittymä
Luokaa nyt sieppaavan reitin sivu. Se hahmonnetaan @modal-slotin sisällä, kun käyttäjä navigoi pehmeästi (käyttämällä komponenttia <Link>) osoitteeseen /photos/[id].
Se vastaanottaa samat params-parametrit kuin varsinainen sivu ja voi hakea samat tiedot. Modaalin kääre on vain komponentti — te hallitsette peittokuvan tyylittelyn.
// 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>
);
}Modal-komponentin rakentaminen router.back()-kutsulla
Modal-komponentin on suljettava itsensä, kun käyttäjä sulkee sen. Idiomaattinen tapa on käyttää kutsua router.back() — se siirtyy historiassa taaksepäin, poistaa siepatun reitin ja palauttaa galleriasivun ilman täydellistä uudelleenlatausta.
Käyttäkää 'use client'-direktiiviä, koska useRouter on asiakaspuolen hook.
// 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>
);
}Kokonainen kuvasivu: suoran navigoinnin varareitti
Itsenäinen /photos/[id]-sivu näytetään käyttäjille, kun he siirtyvät URL-osoitteeseen suoraan — jaetusta linkistä, selainpäivityksen jälkeen tai hakukoneen indeksoijan kautta. Se hahmontaa koko sivuasettelun ilman modaalin käärettä.
Tämä sivu ja siepattu sivu voivat käyttää samaa tiedonhakufunktiota (getPhoto) — datakerrosta ei tarvitse toistaa.
// 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>
);
}Galleriasivu: linkittäminen modaaliin
Kotisivun galleriasivu linkittää osoitteeseen /photos/[id] tavallisella <Link>-komponentilla. Next.js tunnistaa sieppaavan reitin automaattisesti — linkille ei tarvitse määrittää mitään erityistä propia.
Kun JavaScript on käytössä ja käyttäjä napsauttaa linkkiä, sieppaus käynnistyy. Ilman JavaScriptiä (tai ensimmäisellä latauskerralla) koko sivu latautuu normaalisti.
// 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>
);
}(..)-käytännön käyttäminen yhden tason ylempää sieppaamiseen
(..)-etuliite sieppaa reitin, joka on URL-puussa yhden segmentin ylempänä kuin slotin sijainti.
Tarkastellaan tuotesivua osoitteessa /shop/[category]/[productId]. Haluatte, että tuotteen napsauttaminen osoitteen /shop/[category] sisällä avaa modaalin. @modal-slot sijaitsee kansion app/shop/[category]/ sisällä, mutta kohdereitti on myös yhden tason alempana — siksi käytätte (..)-etuliitettä siirtyäksenne yhden tason ylemmäs ja siepataksenne reitin sieltä.
Esimerkki tiedostopolusta:
app/shop/[category]/\n @modal/\n default.tsx\n (..)shop/[category]/[productId]/\n page.tsx ← intercepts the product page\nNyrkkisääntö: laskekaa, kuinka monta segmenttitasoa @modal-kansion ja kohdereitin välillä on, ja käyttäkää yhtä monta pistettä.
Latauskäyttöliittymä ja Suspense siepatuissa reiteissä
Siepattujen reittien sivut ovat asynkronisia Server Components -komponentteja, joten voitte kääriä ne Suspense-komponenttiin ja näyttää latauksen aikana luurankonäkymän tietoja haettaessa — aivan kuten millä tahansa muullakin Next.js-reitillä.
Lisätkää siepatun sivun viereen loading.tsx-tiedosto tai käärikää sisältö <Suspense fallback={...}>-komponenttiin itse modal-komponentissa.
// 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>
);
}Tietotesti: mitä käytäntöä kannattaa käyttää?
Testatkaa ymmärrystänne sieppaavien reittien käytännöistä.
Oppitunnin kertaus: sieppaavat reitit modaalikokemuksia varten
Tässä oppitunnissa opitte toteuttamaan URL-osoitteen säilyttävän modaalimallin Next.js 15:ssä sieppaavien reittien avulla.
- (.)-käytäntö sieppaa reitin samalta segmenttitasolta; (..) siirtyy yhden tason ylemmäs; (...) siirtyy app-juureen.
- Sieppaavat reitit sijaitsevat aina rinnakkaisessa reittislotissa (
@modal), jotta ylemmän tason layout voi hahmontaa taustalla olevan sivun ja modaalin samanaikaisesti. - Slotissa tarvitaan
default.tsx-tiedosto, joka palauttaanull-arvon, jotta 404-virheitä ei synny silloin, kun aktiivista modaalia ei ole. - Modaalin voi sulkea kutsulla
router.back(), joka poistaa siepatun reitin historiasta ilman täydellistä uudelleenlatausta. - Kokonainen itsenäinen sivu on edelleen olemassa todellisessa reittipolussa — selainpäivitys ja jaetut linkit lataavat aina kokonaisen sivun, joten URL-osoite on aidosti jaettavissa.
- Lisätkää siepatun reitin sisälle
loading.tsxtai Suspense, jotta asynkronisten tietojen lataamisen aikana voidaan näyttää luurankonäkymä.
Tämä malli tekee käyttökokemuksesta nopean ja sovellusmaisen, mutta pitää sivut hakukoneystävällisinä ja kaikkien linkin saaneiden saavutettavina.
Opi TypeScript tekoälytuutorin avulla — ilmaiseksi
Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.
- Kurssit
- 22
- Oppitunnit
- 88
Usein kysytyt kysymykset
Onko oppitunti ”Reittien sieppaus jaetuille modaalinäkymille” ilmainen?
Kyllä — voit lukea täällä verkossa kokonaan ilmaiseksi mitkä tahansa Next.js 15 -fullstack-kehitys (App Router + Server Actions)-oppimispolun 3 oppituntia, myös oppitunnin “Reittien sieppaus jaetuille modaalinäkymille”. Sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä interaktiiviset harjoitukset sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Next.js 15 -fullstack-kehitys (App Router + Server Actions)-kurssilla on yhteensä 4 oppituntia.
Mitä opin oppitunnilla ”Reittien sieppaus jaetuille modaalinäkymille”?
Käyttäkää (.)- ja (..)-käytäntöjä sisällön näyttämiseen modaalina säilyttäen jaettavan URL-osoitteen. Harjoittelet Next.js 15 -fullstack-kehitys (App Router + Server Actions)-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.
Tarvitsenko kokemusta aloittaakseni Next.js 15 -fullstack-kehitys (App Router + Server Actions)-opiskelun?
Aiempi kokemus ei ole tarpeen. CoddyKitin Next.js 15 -fullstack-kehitys (App Router + Server Actions)-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 2/4.
Kuinka kauan ”Reittien sieppaus jaetuille modaalinäkymille”-oppitunnin suorittaminen kestää?
Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.
Voinko kirjoittaa ja suorittaa koodia tällä Next.js 15 -fullstack-kehitys (App Router + Server Actions)-oppitunnilla?
Kyllä. Jokainen Next.js 15 -fullstack-kehitys (App Router + Server Actions)-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.
Kaikki tämän kurssin oppitunnit
- Rinnakkaiset reitit nimetyillä sloteilla ja oletussegmenteillä
- Reittien sieppaus jaetuille modaalinäkymille
- Ehdollinen slot-renderöinti koontinäytöille ja välilehdille
- Valokuvamodaalin rakentaminen pehmeällä navigoinnilla