Server-Side Rendering pro SPA: Jak zajistit indexaci ve vyhledávačích
Vyhledávačové roboty jako Googlebot nebo YandexBot často u SPA aplikací založených na Client-Side Rendering (CSR) vidí prázdný HTML kód. Bez Server-Side Renderingu (SSR) nečekají na dokončení spuštění JavaScriptu a opouštějí stránku, aniž by ji zařadily do indexu. SSR řeší tento problém tím, že kompletní HTML generuje na serveru ještě před odesláním klientovi. To urychluje FCP, LCP i TTFB, zlepšuje Core Web Vitals a zvyšuje viditelnost ve výsledcích vyhledávání.
Porovnání metod vykreslování
| Parametr | CSR | SSR | Předvykreslení (Prerendering) | Statické generování (SSG) |
|----------|-----|-----|------------------------------|-----------------------------|
| Místo vykreslování | Prohlížeč | Server (pro každý požadavek) | Předem pro konkrétní URL | Předem pro všechny stránky |
| FCP/LCP | Pomalé | Rychlé | Rychlé | Velmi rychlé |
| SEO | Slabé | Vynikající | Dobré | Vynikající |
| Aktualita dat | V reálném čase | V reálném čase | Zastaralá | Zastaralá |
| Zátěž serveru | Minimální | Vysoká | Nízká | Minimální |
SSR je ideální pro dynamické stránky — např. katalogy produktů nebo novinky. Předvykreslení se hodí pro málo měnící se URL. SSG je nejvhodnější pro statický obsah.
React + Next.js: Postupná implementace
Vytvořte projekt:
npx create-next-app@latest my-ssr-app
cd my-ssr-app
npm run dev
Pro dynamické stránky použijte getServerSideProps:
// pages/product/[id].jsx
export async function getServerSideProps(context) {
const { id } = context.params;
const res = await fetch(`https://api.example.com/products/${id}`);
const product = await res.json();
if (!res.ok) {
return { notFound: true };
}
return {
props: { product },
};
}
export default function ProductPage({ product }) {
return (
<main>
<h1>{product.name}</h1>
<p>{product.description}</p>
</main>
);
}
Přidejte meta-tagy:
import Head from 'next/head';
export default function ProductPage({ product }) {
return (
<>
<Head>
<title>{product.name} | E-shop</title>
<meta name="description" content={product.shortDescription} />
<meta property="og:title" content={product.name} />
<meta property="og:image" content={product.imageUrl} />
</Head>
<main>
<h1>{product.name}</h1>
</main>
</>
);
}
Nasazení: Vercel, Netlify nebo Node.js s PM2.
Hydratace bez chyb
Hydratace je proces, při němž klientní React „oživuje“ HTML vygenerované na serveru. Abyste se vyhnuli nesouladu (mismatch), dodržujte tyto zásady:
- Synchronizujte data mezi serverem a klientem.
- Před použitím
windownebodocumentověřte prostředí. - Sledujte konzoli na varování.
const isBrowser = typeof window !== 'undefined';
Vue + Nuxt.js: Serverové vykreslování
Vytvořte projekt:
npx nuxi@latest init my-nuxt-app
cd my-nuxt-app
npm install
npm run dev
Získání dat na serveru:
<!-- pages/product/[id].vue -->
<script setup>
const route = useRoute();
const { data: product, error } = await useFetch(
`/api/products/${route.params.id}`
);
if (error.value) {
throw createError({ statusCode: 404, message: 'Stránka nenalezena' });
}
useHead({
title: product.value?.name,
meta: [
{ name: 'description', content: product.value?.shortDescription },
],
});
</script>
<template>
<main>
<h1>{{ product?.name }}</h1>
<p>{{ product?.description }}</p>
</main>
</template>
Konfigurace nuxt.config.ts:
export default defineNuxtConfig({
ssr: true,
app: {
head: {
htmlAttrs: { lang: 'cs' },
link: [{ rel: 'canonical', href: 'https://example.com' }],
},
},
nitro: {
preset: 'node-server',
},
});
Angular Universal: Integrace SSR
Pro Angular 17 a novější:
ng add @angular/ssr
Serverový endpoint v server.ts:
import { APP_BASE_HREF } from '@angular/common';
import { CommonEngine } from '@angular/ssr';
import express from 'express';
// ... ostatní importy
const server = express();
// ...
server.get('*', (req, res, next) => {
commonEngine
.render({
bootstrap: AppServerModule,
documentFilePath: join(browserDistFolder, 'index.html'),
url: req.originalUrl,
publicPath: browserDistFolder,
providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }],
})
.then((html) => res.send(html))
.catch(next);
});
Sestavení:
npm run build:ssr
npm run serve:ssr
Ověření platformy:
import { isPlatformBrowser } from '@angular/common';
import { inject, PLATFORM_ID } from '@angular/core';
const platformId = inject(PLATFORM_ID);
if (isPlatformBrowser(platformId)) {
console.log(window.location.href);
}
Integrace s Headless CMS
Pro Next.js a Contentful pomocí GraphQL:
export async function getServerSideProps({ params }) {
const query = `
query GetArticle($slug: String!) {
articleCollection(where: { slug: $slug }) {
items {
title
body
seo {
title
description
}
}
}
}
`;
const res = await fetch(
`https://graphql.contentful.com/content/v1/spaces/${process.env.CONTENTFUL_SPACE_ID}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.CONTENTFUL_TOKEN}`,
},
body: JSON.stringify({ query, variables: { slug: params.slug } }),
}
);
const { data } = await res.json();
// ...
}
Klíčové body
- SSR generuje plnohodnotný HTML pro vyhledávačové roboty a tak řeší hlavní nedostatky indexace CSR.
- Next.js, Nuxt.js a Angular Universal jsou ověřené frameworky pro snadnou implementaci SSR.
- Nesoulad při hydrataci (hydration mismatch) způsobuje přepracování DOMu — vždy ověřujte kontext spuštění.
- Core Web Vitals (FCP, LCP, TTFB) se po přechodu na SSR zlepší o 50–80 %.
- Pro vysokou zátěž doporučujeme cachování přes Redis, aby byl TTFB pod 200 ms.
— Editorial Team
Zatím žádné komentáře.