Next.js SEO: Kdy zvolit SSR, SSG nebo ISR pro lepší viditelnost
Postavili jste web v Reactu a Google ho neviděl. Hodiny práce, krásný design, interaktivní komponenty - a v Search Console nula indexovaných stránek.
Tento problém znám z praxe. Čistý React renderuje obsah v prohlížeči. Googlebot přijde, vidí prázdnou stránku a odchází. Řešení? Next.js.
Next.js je React framework, který řeší největší bolest JavaScriptových webů - viditelnost ve vyhledávačích. Za posledních 5 let se stal standardem pro weby, kde záleží na SEO.
V tomto průvodci vám ukážu, jak funguje vykreslování na serveru (SSR), na klientovi (CSR) a generování statických stránek (SSG), kdy který režim použít, a jak správně nastavit meta tagy. Všechno s praktickými příklady kódu.
Proč je Next.js oblíbený pro SEO?
Problém s čistým Reactem
Klasická React aplikace funguje takto:
- server pošle prázdný HTML soubor
- Prohlížeč stáhne JavaScript bundle (často 500+ KB)
- JavaScript se spustí a vykreslí obsah
- Teprve teď uživatel (a Googlebot) vidí stránku
<html>
<head><title>Můj web</title></head>
<body>
<div id="root"></div>
<script src="bundle.js"></script>
</body>
</html>
Googlebot umí spouštět JavaScript, ale:
- Má časový limit - medián doby renderování je 5 sekund, 90. percentil může být minuty (zdroj: Onely, Google Chrome Developer Summit 2019)
- Spotřebovává rozpočet crawlování - renderování JavaScriptu je náročné na zdroje, výzkum ukazuje 9× delší dobu crawlování pro JS obsah (zdroj: Onely)
- Může chybět dynamický obsah - data z API se nemusí načíst včas
Výsledek? Weby postavené na čistém Reactu mají problémy s crawlováním a indexací.
Jak to řeší Next.js
Next.js nabízí tři způsoby, jak dostat HTML ke Googlebotu ještě před spuštěním JavaScriptu:
Každý režim má své místo. Neexistuje univerzální odpověď - záleží na typu obsahu.
Tři režimy vykreslování v Next.js
Generování statických stránek - SSG (getStaticProps)
SSG generuje HTML soubory jednou při buildu. Když uživatel (nebo Googlebot) přijde na stránku, server mu pošle hotový HTML soubor. Žádné čekání, žádné vykreslování.
Jak to funguje v kódu (Pages Router):
// pages/blog/[slug].js
export async function getStaticProps({ params }) {
const post = await getPostBySlug(params.slug);
return {
props: {
post,
},
};
}
export async function getStaticPaths() {
const posts = await getAllPosts();
return {
paths: posts.map((post) => ({
params: { slug: post.slug },
})),
fallback: false,
};
}
export default function BlogPost({ post }) {
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
);
}
V App Router (Next.js 13+):
// app/blog/[slug]/page.js
export async function generateStaticParams() {
const posts = await getAllPosts();
return posts.map((post) => ({
slug: post.slug,
}));
}
export default async function BlogPost({ params }) {
const post = await getPostBySlug(params.slug);
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
);
}
Výhody SSG:
- Extrémně rychlé načítání (servíruje statický soubor)
- Nízká zátěž serveru
- Vynikající Core Web Vitals - LCP pod 2.5s je označeno jako "dobré" (zdroj: web.dev)
- Snadné cachování na CDN
Nevýhody SSG:
- Při změně obsahu nutný rebuild
- Nevhodné pro často se měnící data
- Při velkém počtu stránek dlouhý build
Benchmark rychlosti v roce 2025 (zdroj: Medium - 2025 Performance Benchmarks):
Doporučené cíle: TTFB pod 100 ms pro statický obsah, 100-300 ms tolerovatelné pro SSR (zdroj: web.dev)
Vykreslování na serveru - SSR (getServerSideProps)
SSR generuje HTML při každém požadavku. Server přijme request, stáhne data z databáze/API, vygeneruje HTML a pošle ho klientovi.
Kód (Pages Router):
// pages/produkt/[id].js
export async function getServerSideProps({ params, req }) {
const product = await getProductById(params.id);
const userLocation = req.headers['x-forwarded-for'];
const localPrice = await getLocalPrice(product.price, userLocation);
return {
props: {
product: {
...product,
price: localPrice,
},
},
};
}
export default function ProductPage({ product }) {
return (
<div>
<h1>{product.name}</h1>
<p>Cena: {product.price} Kč</p>
<p>Dostupnost: {product.inStock ? 'Skladem' : 'Na objednávku'}</p>
</div>
);
}
V App Router (Next.js 13+):
// app/produkt/[id]/page.js
export const dynamic = 'force-dynamic';
export default async function ProductPage({ params }) {
const product = await getProductById(params.id);
return (
<div>
<h1>{product.name}</h1>
<p>Cena: {product.price} Kč</p>
</div>
);
}
Výhody SSR:
- Vždy čerstvý obsah
- Personalizovaný obsah (podle uživatele, lokality)
- Žádné rebuildy při změně dat
Nevýhody SSR:
- Vyšší zátěž serveru
- Pomalejší TTFB než SSG
- Složitější cachování
Inkrementální statická regenerace - ISR
ISR kombinuje výhody SSG a SSR. Stránky se vygenerují při buildu (jako SSG), ale mohou se automaticky aktualizovat v pozadí.
Kód (Pages Router):
// pages/produkty/[category].js
export async function getStaticProps({ params }) {
const products = await getProductsByCategory(params.category);
return {
props: {
products,
},
revalidate: 3600, // Aktualizace každou hodinu
};
}
export async function getStaticPaths() {
const categories = await getAllCategories();
return {
paths: categories.map((cat) => ({
params: { category: cat.slug },
})),
fallback: 'blocking', // Nové stránky se generují při prvním přístupu
};
}
V App Router:
// app/produkty/[category]/page.js
export const revalidate = 3600; // Aktualizace každou hodinu
export async function generateStaticParams() {
const categories = await getAllCategories();
return categories.map((cat) => ({
category: cat.slug,
}));
}
export default async function CategoryPage({ params }) {
const products = await getProductsByCategory(params.category);
return (
<div>
<h1>Produkty v kategorii {params.category}</h1>
{products.map((p) => <ProductCard key={p.id} product={p} />)}
</div>
);
}
Jak ISR funguje:
- Při buildu se vygenerují statické HTML soubory
- Uživatel přijde na stránku - dostane cachovaný HTML
- Po uplynutí
revalidatečasu Next.js na pozadí regeneruje stránku - Další uživatel dostane nový HTML
Výhody ISR:
- Rychlost SSG + čerstvý obsah
- Nenačítá server při každém requestu
- Ideální pro velké weby (stovky až tisíce stránek) - firmy reportují 70-85% zkrácení doby buildu při přechodu na ISR (zdroj: Medium - 2025 Performance Benchmarks)
Nevýhody ISR:
- Obsah může být stále až do revalidace
- Složitější debugging
- Nejlépe funguje na Vercelu
Kdy použít který režim?
SSG: Blogy a marketingové stránky
Použijte SSG pro obsah, který se nemění často:
- Blogové články
- Vstupní stránky (landing pages)
- Dokumentace
- O nás, Kontakt
- Právní dokumenty (GDPR, Podmínky)
Příklad z praxe: Blog se 100 články. Obsah se mění jednou týdně. SSG vygeneruje 100 HTML souborů při buildu. Rychlost načítání: LCP pod 2.5 sekundy (doporučený práh Google).
SSR: E-shopy a dashboardy
Použijte SSR pro:
- Stránky produktů s aktuální cenou a dostupností
- Personalizovaný obsah
- Uživatelské dashboardy
- Stránky závislé na session/cookies
- Real-time data
Příklad z praxe: E-shop s 10 000 produkty. Ceny se mění několikrát denně. SSR zajistí, že Googlebot i zákazník vidí vždy aktuální cenu.
ISR: Velké weby s častou aktualizací
Použijte ISR pro:
- Katalogy produktů (aktualizace co hodinu)
- Zpravodajské weby
- Marketplace platformy
- Stránky s user-generated contentem
Příklad z praxe: Bazar s desítkami tisíc inzerátů. Build všech stránek by trval hodiny. ISR vygeneruje nejdůležitější stránky při buildu, zbytek on-demand.
Rozhodovací tabulka
Meta tagy v Next.js
Správně nastavené meta tagy jsou základ on-page SEO. Next.js nabízí několik způsobů.
Next.js Metadata API (App Router)
Od Next.js 13 je doporučený způsob přes Metadata API:
// app/blog/[slug]/page.js
export async function generateMetadata({ params }) {
const post = await getPostBySlug(params.slug);
return {
title: post.seoTitle || post.title,
description: post.seoDescription || post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
images: [post.image],
type: 'article',
publishedTime: post.publishedAt,
authors: [post.author],
},
twitter: {
card: 'summary_large_image',
title: post.title,
description: post.excerpt,
images: [post.image],
},
alternates: {
canonical: `https://example.com/blog/${params.slug}`,
},
};
}
next/head komponenta (Pages Router)
Pro Pages Router použijte komponentu next/head:
// pages/blog/[slug].js
import Head from 'next/head';
export default function BlogPost({ post }) {
return (
<>
<Head>
<title>{post.seoTitle || post.title}</title>
<meta name="description" content={post.seoDescription} />
<link rel="canonical" href={`https://example.com/blog/${post.slug}`} />
{/* Open Graph */}
<meta property="og:title" content={post.title} />
<meta property="og:description" content={post.excerpt} />
<meta property="og:image" content={post.image} />
<meta property="og:type" content="article" />
{/* Twitter Cards */}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={post.title} />
<meta name="twitter:description" content={post.excerpt} />
<meta name="twitter:image" content={post.image} />
</Head>
<article>
<h1>{post.title}</h1>
{/* ... */}
</article>
</>
);
}
Více o psaní kvalitních meta tagů najdete v článku Meta title a description.
next-seo knihovna
Pro složitější projekty doporučuji knihovnu next-seo:
npm install next-seo
// pages/_app.js
import { DefaultSeo } from 'next-seo';
export default function App({ Component, pageProps }) {
return (
<>
<DefaultSeo
titleTemplate="%s | Můj web"
defaultTitle="Můj web"
openGraph={{
type: 'website',
locale: 'cs_CZ',
site_name: 'Můj web',
}}
/>
<Component {...pageProps} />
</>
);
}
// pages/blog/[slug].js
import { NextSeo, ArticleJsonLd } from 'next-seo';
export default function BlogPost({ post }) {
return (
<>
<NextSeo
title={post.title}
description={post.excerpt}
canonical={`https://example.com/blog/${post.slug}`}
openGraph={{
title: post.title,
description: post.excerpt,
images: [{ url: post.image, alt: post.title }],
article: {
publishedTime: post.publishedAt,
authors: [post.author],
},
}}
/>
<ArticleJsonLd
title={post.title}
datePublished={post.publishedAt}
dateModified={post.updatedAt}
authorName={post.author}
description={post.excerpt}
images={[post.image]}
url={`https://example.com/blog/${post.slug}`}
/>
{/* ... */}
</>
);
}
Technické SEO v Next.js
Sitemap s next-sitemap
Automatická generace sitemapy:
npm install next-sitemap
// next-sitemap.config.js
module.exports = {
siteUrl: 'https://example.com',
generateRobotsTxt: true,
sitemapSize: 7000,
changefreq: 'weekly',
priority: 0.7,
exclude: ['/admin/*', '/api/*'],
robotsTxtOptions: {
additionalSitemaps: [
'https://example.com/server-sitemap.xml',
],
policies: [
{ userAgent: '*', allow: '/' },
{ userAgent: '*', disallow: ['/admin', '/api'] },
],
},
transform: async (config, path) => {
// Vlastní priorita pro důležité stránky
if (path === '/') {
return {
loc: path,
changefreq: 'daily',
priority: 1.0,
};
}
if (path.startsWith('/blog/')) {
return {
loc: path,
changefreq: 'monthly',
priority: 0.8,
};
}
return {
loc: path,
changefreq: config.changefreq,
priority: config.priority,
};
},
};
Přidejte do package.json:
{
"scripts": {
"postbuild": "next-sitemap"
}
}
Robots.txt
Next-sitemap vygeneruje robots.txt automaticky. Pro vlastní nastavení:
// next-sitemap.config.js
module.exports = {
// ...
robotsTxtOptions: {
policies: [
{ userAgent: '*', allow: '/' },
{ userAgent: 'Googlebot', allow: '/' },
{ userAgent: '*', disallow: ['/admin', '/api', '/checkout'] },
],
},
};
Canonical URL
Canonical tag zabraňuje duplicitnímu obsahu. V Next.js nastavte pro každou stránku:
// Metadata API
export const metadata = {
alternates: {
canonical: 'https://example.com/stranka',
},
};
// nebo dynamicky
export async function generateMetadata({ params }) {
return {
alternates: {
canonical: `https://example.com/blog/${params.slug}`,
},
};
}
Strukturovaná data (JSON-LD)
Schema markup pomáhá Googlu pochopit obsah. Implementace v Next.js:
// components/JsonLd.js
export default function JsonLd({ data }) {
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
/>
);
}
// použití na stránce produktu
import JsonLd from '@/components/JsonLd';
export default function ProductPage({ product }) {
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'Product',
name: product.name,
description: product.description,
image: product.images,
offers: {
'@type': 'Offer',
price: product.price,
priceCurrency: 'CZK',
availability: product.inStock
? 'https://schema.org/InStock'
: 'https://schema.org/OutOfStock',
},
aggregateRating: {
'@type': 'AggregateRating',
ratingValue: product.rating,
reviewCount: product.reviewCount,
},
};
return (
<>
<JsonLd data={jsonLd} />
{/* zbytek stránky */}
</>
);
}
Rychlost a Core Web Vitals
Next.js má vestavěné nástroje pro optimalizaci rychlosti.
Next.js Image komponenta
Automatická optimalizace obrázků:
import Image from 'next/image';
export default function Hero() {
return (
<Image
src="/hero.jpg"
alt="Popis obrázku"
width={1200}
height={630}
priority // Pro obrázky nad ohybem
placeholder="blur"
blurDataURL="/hero-blur.jpg"
/>
);
}
Co dělá Image komponenta:
- Automaticky konvertuje do WebP/AVIF
- Generuje srcset pro různé velikosti
- Lazy loading pod ohybem
- Zabraňuje CLS (rezervuje místo)
Optimalizace fontu
// app/layout.js (App Router)
import { Inter } from 'next/font/google';
const inter = Inter({
subsets: ['latin', 'latin-ext'],
display: 'swap',
});
export default function RootLayout({ children }) {
return (
<html lang="cs" className={inter.className}>
<body>{children}</body>
</html>
);
}
Výhody Next.js fontů:
- Zero layout shift
- Self-hosted (žádné externí requesty)
- Automaticky optimalizované
Bundle size
Monitorujte velikost bundlu:
ANALYZE=true npm run build
Konfigurace:
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});
module.exports = withBundleAnalyzer({
// vaše konfigurace
});
Tipy pro menší bundle:
- Dynamic imports pro těžké komponenty
- Tree shaking (importujte jen co potřebujete)
- Vyhýbejte se velkým knihovnám (moment.js → date-fns)
// Dynamic import
import dynamic from 'next/dynamic';
const HeavyChart = dynamic(() => import('@/components/HeavyChart'), {
loading: () => <p>Načítám graf...</p>,
ssr: false,
});
Hosting a deployment
Vercel - optimální volba
Vercel je tvůrce Next.js a nabízí:
- Automatický deployment z Gitu
- Edge caching pro SSG/ISR
- Serverless functions pro SSR
- Analytics a monitoring
- Preview deployments pro každou branch
Pro většinu projektů je Vercel nejlepší volba. Free tier stačí pro menší projekty.
Self-hosting
Next.js můžete hostovat i sami:
Node.js server:
npm run build
npm start
Docker:
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:18-alpine AS runner
WORKDIR /app
ENV NODE_ENV production
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
EXPOSE 3000
CMD ["node", "server.js"]
// next.config.js pro Docker
module.exports = {
output: 'standalone',
};
Omezení self-hostingu:
- ISR vyžaduje dodatečnou konfiguraci
- Chybí edge caching (museli byste řešit sami CDN)
- Složitější monitoring
Časté chyby a jak se jim vyhnout
1. Client-only rendering
Chyba: Používání useEffect pro načtení dat.
// ŠPATNĚ - Google nevidí obsah
export default function Page() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('/api/data').then(r => r.json()).then(setData);
}, []);
if (!data) return <p>Načítám...</p>;
return <div>{data.title}</div>;
}
Řešení: Použijte SSR nebo SSG.
// SPRÁVNĚ
export async function getServerSideProps() {
const data = await fetch('https://api.example.com/data').then(r => r.json());
return { props: { data } };
}
export default function Page({ data }) {
return <div>{data.title}</div>;
}
2. Chybějící meta tagy
Chyba: Zapomenout na title a description.
Řešení: Vytvořte společnou layout komponentu s defaultními meta tagy.
3. Špatný režim pro typ obsahu
Chyba: SSG pro e-shop s měnícími se cenami.
Důsledek: Googlebot indexuje starou cenu → špatná uživatelská zkušenost → potenciální právní problémy.
Řešení: Pro dynamický obsah použijte SSR nebo ISR s krátkým revalidate časem.
4. Špatně nastavený canonical
Chyba: Různé URL vedou na stejný obsah bez canonical.
/produkt/123
/produkt/123?utm_source=newsletter
/produkt/123?ref=homepage
Řešení: Vždy nastavte canonical na hlavní URL bez parametrů.
5. Chybějící fallback u ISR
Chyba: fallback: false u velkých webů.
Důsledek: Nové stránky nejsou dostupné, dokud nebude rebuild.
Řešení: Použijte fallback: 'blocking' pro generování stránek on-demand.
Reálné příklady webů na Next.js
E-commerce: Vercel Commerce
Vercel Commerce je open-source e-shop šablona:
- SSR pro stránky produktů (aktuální ceny)
- ISR pro kategorie (revalidate: 300)
- SSG pro statické stránky
Typické výsledky pro optimalizované Next.js e-shopy:
- LCP: pod 2.5s (doporučený práh Google - zdroj: web.dev)
- Lighthouse Performance: 85+ (při správné konfiguraci)
Poznámka: Konkrétní výsledky závisí na infrastruktuře, velikosti stránek a optimalizaci obrázků.
Blog: Hashnode
Hashnode je blogovací platforma postavená na Next.js:
- SSG pro články
- ISR pro homepage (nové články)
- Extrémně rychlé načítání
Dokumentace: Next.js Docs
Oficiální dokumentace Next.js:
- Plně SSG
- Instant načítání
- Vynikající SEO
Závěr
Next.js řeší největší problém JavaScript SEO - viditelnost ve vyhledávačích. Ale správná volba renderovacích režimů je klíčová.
Shrnutí:
- SSG pro statické stránky (blogy, vstupní stránky)
- SSR pro dynamický obsah (e-shopy, dashboardy)
- ISR pro velké weby s častou aktualizací
3 věci, které můžete udělat dnes:
- Zkontrolujte v Search Console, zda Google vidí váš obsah
- Nastavte správné meta tagy pro všechny stránky
- Změřte Core Web Vitals a optimalizujte obrázky
Potřebujete pomoct s technickým SEO pro Next.js?
Pomáhám firmám optimalizovat weby na Next.js a dosáhnout lepších výsledků.
Související články
- SSR vs CSR vs SSG: který rendering zvolit pro lepší SEO?
- JavaScript SEO: CSR vs SSR a jak zlepšit indexaci
- Core Web Vitals: LCP, INP a CLS jednoduše
- Schema markup: 5 typů strukturovaných dat pro vyšší CTR
- Meta title a description: Jak psát pro vyšší CTR
Často kladené otázky (FAQ)
Je Next.js lepší pro SEO než čistý React?
Ano, výrazně. Čistý React (Create React App) používá client-side rendering - Googlebot musí spustit JavaScript, aby viděl obsah. To spotřebovává rozpočet crawlování a může způsobit problémy s indexací.
Next.js nabízí SSR, SSG a ISR, které posílají hotový HTML ještě před spuštěním JavaScriptu. Google obsah vidí okamžitě, indexace je rychlejší a spolehlivější.
V praxi vidím rozdíl v rychlosti indexace: Next.js stránky se indexují během dnů, React SPA často až po týdnech (pokud se vůbec indexují). Výzkum Vercel a MERJ z roku 2024 analyzoval přes 100 000 požadavků Googlebotu a potvrdil, že SSR obsah je plně indexovatelný bez nutnosti spouštět JavaScript na straně klienta (zdroj: Vercel Blog).
Jaký je rozdíl mezi SSR a SSG?
Klíčový rozdíl je kdy se HTML generuje:
Generování statických stránek (SSG):
- HTML se vygeneruje jednou při buildu
- Každý uživatel dostane stejný soubor
- Extrémně rychlé (TTFB 20-50 ms s CDN cache)
- Vhodné pro statické stránky
Vykreslování na serveru (SSR):
- HTML se generuje při každém requestu
- Obsah může být personalizovaný
- Pomalejší (TTFB 100-500 ms dle infrastruktury)
- Vhodné pro dynamický obsah
Volba závisí na typu obsahu. Blog? SSG. E-shop s aktuální dostupností? SSR.
Co je ISR a kdy ho použít?
ISR (inkrementální statická regenerace) kombinuje výhody SSG a SSR. Stránky se vygenerují při buildu, ale mohou se automaticky aktualizovat na pozadí.
Použijte ISR když:
- Máte velký web (stovky až tisíce stránek)
- Obsah se mění pravidelně, ale ne v realtime
- Potřebujete rychlost SSG s aktuálností SSR
Příklad: Katalog produktů s tisíci položkami. SSG by build trval hodiny. SSR by zatěžoval server. ISR vygeneruje stránky postupně a aktualizuje je co hodinu.
Nastavení: revalidate: 3600 (aktualizace co hodinu).
Jak přidat meta tagy v Next.js?
Záleží na verzi Next.js:
App Router (Next.js 13+): Použijte Metadata API
export const metadata = {
title: 'Název stránky',
description: 'Popis stránky',
};
Pages Router: Použijte next/head
import Head from 'next/head';
<Head>
<title>Název stránky</title>
<meta name="description" content="Popis stránky" />
</Head>
Pro složitější projekty doporučuji knihovnu next-seo, která zjednodušuje práci s Open Graph, Twitter Cards a strukturovanými daty.
Mohu Next.js hostovat jinde než na Vercel?
Ano, Next.js můžete hostovat prakticky kdekoliv:
- VPS/dedikák:
npm run build && npm start - Docker: Oficiální Dockerfile s
output: 'standalone' - Netlify: Podporuje SSG i SSR
- AWS Amplify: Serverless hosting
- Railway, Render, Fly.io: Moderní hosting platformy
Pozor: Některé funkce fungují lépe na Vercelu:
- ISR na jiné platformě vyžaduje dodatečnou konfiguraci
- Edge functions jsou limitované
- Image optimization vyžaduje vlastní nastavení
Pro jednoduché SSG stránky stačí jakýkoliv statický hosting. Pro plné využití SSR/ISR je Vercel nejjednodušší volba.
O autorce

Ing. Jana Hrabalová
SEO specialistka
SEO se věnuji od roku 2012. Pomáhám firmám získat více zákazníků z Google a přežít každý algoritmus update bez škrábnutí.
📚 Čtěte dále
Získejte SEO článek zdarma
Publikuji váš článek na kvalitním webu s vysokou autoritou
- Publikace na webu s DA 50+
- Dofollow odkaz na váš web
- Profesionální copywriting
Vyzkoušejte také mé bezplatné SEO nástroje: