07 — Dynamické routy ([id]/page.js)
Výuka
Proč to existuje?
Pokud máš blog se 100 články, nechceš vytvářet 100 samostatných souborů (clanek-1/page.js, clanek-2/page.js, atd.). Potřebuješ jednu šablonu, která funguje pro všechny články — mění se jen ID nebo slug v URL.
Dynamické routy umožňují vytvořit jednu page komponentu, která obslouží neomezeně mnoho URL s různými parametry.
Jak to funguje?
V Next.js vytvoříš dynamickou routu pomocí hranatých závorek [parametr] v názvu složky:
app/blog/[slug]/page.js→ obslouží/blog/prvni-clanek,/blog/druhy-clanek, atd.app/products/[id]/page.js→ obslouží/products/1,/products/2,/products/999, atd.
Next.js automaticky:
- Zachytí parametr z URL (např.
slugneboid) - Předá ho page komponentě jako prop
params - Ty použiješ parametr k načtení správných dat
K čemu to slouží?
- Škálovatelnost — jedna komponenta pro tisíce stránek
- DRY princip — nepotřebuješ opakovat kód
- Databázově řízený obsah — načteš data podle ID z URL
- SEO friendly — každý článek má vlastní URL
JavaScript
// app/blog/[slug]/page.js
export default function BlogPost({ params }) {
// params.slug obsahuje hodnotu z URL
// Například: /blog/muj-prvni-clanek → params.slug === "muj-prvni-clanek"
return (
<article>
<h1>Článek: {params.slug}</h1>
<p>Tady bude obsah článku...</p>
</article>
)
}
// app/products/[id]/page.js
export default async function ProductPage({ params }) {
// Načteme data z API podle ID
const product = await fetch(`https://api.example.com/products/${params.id}`)
.then(res => res.json())
return (
<div>
<h1>{product.name}</h1>
<p>Cena: {product.price} Kč</p>
<p>{product.description}</p>
</div>
)
}
Více dynamických parametrů
// app/users/[userId]/posts/[postId]/page.js
export default function UserPost({ params }) {
// params.userId a params.postId
// URL: /users/123/posts/456
return (
<div>
<h1>Příspěvek #{params.postId}</h1>
<p>od uživatele #{params.userId}</p>
</div>
)
}
TypeScript
// app/blog/[slug]/page.tsx
interface PageProps {
params: { slug: string }
}
export default function BlogPost({ params }: PageProps): JSX.Element {
return (
<article>
<h1>Článek: {params.slug}</h1>
<p>Tady bude obsah článku...</p>
</article>
)
}
// app/products/[id]/page.tsx
interface Product {
id: number
name: string
price: number
description: string
}
interface PageProps {
params: { id: string }
}
export default async function ProductPage({ params }: PageProps): Promise<JSX.Element> {
const product: Product = await fetch(`https://api.example.com/products/${params.id}`)
.then(res => res.json())
return (
<div>
<h1>{product.name}</h1>
<p>Cena: {product.price} Kč</p>
<p>{product.description}</p>
</div>
)
}
Více parametrů s typy
interface PageProps {
params: {
userId: string
postId: string
}
}
export default function UserPost({ params }: PageProps): JSX.Element {
return (
<div>
<h1>Příspěvek #{params.postId}</h1>
<p>od uživatele #{params.userId}</p>
</div>
)
}
Rozdíl JS vs TS
TypeScript přidává typy pro params prop, což zajišťuje:
- Autocomplete v editoru (víš, jaké parametry máš k dispozici)
- Type safety — nemůžeš omylem použít neexistující parametr
- Dokumentaci — ostatní vývojáři ihned vidí, jaké parametry stránka přijímá
generateStaticParams (pro statické generování)
Pro staticky generované stránky (např. všechny články předem) použij generateStaticParams:
// app/blog/[slug]/page.tsx
// Vygeneruje statické stránky při buildu
export async function generateStaticParams() {
const posts = await fetch('https://api.example.com/posts').then(r => r.json())
return posts.map((post) => ({
slug: post.slug,
}))
}
export default async function BlogPost({ params }: { params: { slug: string } }) {
const post = await fetch(`https://api.example.com/posts/${params.slug}`)
.then(r => r.json())
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
)
}
Co to dělá:
- Při buildu Next.js zavolá
generateStaticParams - Získá seznam všech slugů
- Vygeneruje HTML pro každý článek předem
- Výsledek: ultrarychlé načítání (static HTML bez serveru)
Tip
- Pojmenování parametru — používej popisné názvy (
slug,id,userId, ne jenparam) - Validace — ověř, že parametr má správný formát (např. ID je číslo)
- 404 handling — pokud záznam neexistuje, použij
notFound()znext/navigation - URL encoding — parametry jsou automaticky URL decoded (např.
hello%20world→hello world)
Příklad s validací a 404
import { notFound } from 'next/navigation'
export default async function ProductPage({ params }: { params: { id: string } }) {
const id = parseInt(params.id)
// Validace: ID musí být číslo
if (isNaN(id) || id <= 0) {
notFound()
}
const product = await fetch(`https://api.example.com/products/${id}`)
.then(res => res.ok ? res.json() : null)
// Pokud produkt neexistuje, zobraz 404
if (!product) {
notFound()
}
return <div>{product.name}</div>
}
Dynamické segmenty — ASCII diagram
Jak vypadají složky pro dynamické routy:
app/
├─ blog/
│ ├─ page.js # → /blog (seznam článků)
│ └─ [slug]/
│ └─ page.js # → /blog/prvni-clanek, /blog/druhy-clanek
├─ products/
│ ├─ page.js # → /products (seznam produktů)
│ └─ [id]/
│ └─ page.js # → /products/1, /products/2, /products/999
└─ users/
└─ [userId]/
├─ page.js # → /users/123
└─ posts/
└─ [postId]/
└─ page.js # → /users/123/posts/456
Jak to funguje:
- Hranaté závorky
[parametr]= dynamický segment - Hodnota z URL se předá jako
params.parametr - Můžeš mít více dynamických segmentů ve stejné cestě
Kvíz
Jak vytvoříš dynamický segment pro ID produktu v URL /products/123?
V Next.js vytváříš dynamické segmenty pomocí hranatých závorek [parametr] v názvu složky. Například app/products/[id]/page.js vytvoří dynamickou routu, která obslouží /products/1, /products/2, atd. Hodnota z URL (např. 123) se automaticky předá page komponentě jako params.id. Syntaxe :id nebo {id} se používá v jiných frameworcích, ale ne v Next.js.
🎯 Závěrečný projekt
Po dokončení všech 8 dílů vytvoříte jednoduchou Todо aplikaci v čistém JavaScriptu. Naučíte se, jak aplikovat vše, co jste se naučili, na reálný projekt.
Zobrazit podrobnosti projektu →Informace o seriálu
Obtížnost
Délka
Cca 480 minut
Počet videí
8 videí + projekty
Certifikát
Po dokončení obdržíte certifikát
Lekce v této sekci
- 01 — Co je Next.js
- 02 — Vytvoření projektu
- 03 — Struktura projektu (app/)
- 04 — Page komponenty (page.js / page.tsx)
- 05 — Layout komponenty (layout.js / layout.tsx)
- 06 — File-based routing
- 07 — Dynamické routy ([id]/page.js)
- 08 — Link komponenta (navigace)
- 09 — Image komponenta (next/image)
- 10 — Metadata (title, description, Open Graph)
- 11 — Loading UI (loading.js / loading.tsx)
- 12 — Error handling (error.js / error.tsx)
- 13 — Not Found (not-found.js / not-found.tsx)
Struktura lekcí (souborový strom)
- 1.1 Úvod do JavaScriptu a TypeScriptu
- 1.2 Nastavení prostředí
- 1.3 První program
- 1.4 Proměnné: var, let, const
- 1.5 Datové typy - přehled
- 1.6 String (řetězce)
- 1.7 Number (čísla)
- 1.8 Boolean (pravda/nepravda)
- 1.9 Null a Undefined
- 1.10 Type Inference vs Annotations
- 1.11 Aritmetické operátory
- 1.12 Porovnávací operátory
- 1.13 Logické operátory
- 1.14 Komentáře
- 1.15 Console metody
- 03.01 Deklarace funkce
- 03.02 Function Expression
- 03.03 Arrow Functions
- 03.04 Parametry a argumenty
- 03.05 Return hodnoty
- 03.06 Výchozí parametry
- 03.07 Rest parametry
- 03.08 Co je Scope
- 03.09 Lexikální Scope
- 03.10 Řetězec Scope
- 03.11 Globální Scope
- 03.12 Životní Cyklus Proměnných
- 03.13 Omezení Scope
- 03.14 Použití Closures
- 03.15 Callback funkce
- 03.16 Higher-order Functions
- 03.17 IIFE
- 03.18 Void funkce
- 03.19 Rekurze
- v přípravě
- v přípravě
- v přípravě
- v přípravě
- 01 — Co je Next.js
- 02 — Vytvoření projektu
- 03 — Struktura projektu (app/)
- 04 — Page komponenty (page.js / page.tsx)
- 05 — Layout komponenty (layout.js / layout.tsx)
- 06 — File-based routing
- 07 — Dynamické routy ([id]/page.js)
- 08 — Link komponenta (navigace)
- 09 — Image komponenta (next/image)
- 10 — Metadata (title, description, Open Graph)
- 11 — Loading UI (loading.js / loading.tsx)
- 12 — Error handling (error.js / error.tsx)
- 13 — Not Found (not-found.js / not-found.tsx)
- v přípravě
- v přípravě
- v přípravě