import { Metadata } from 'next';
interface Props {
params: { id: string };
}
async function getProduct(id: string) {
const res = await fetch(`https://api.exemplu.ro/products/${id}`);
return res.json();
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const product = await getProduct(params.id);
return {
title: `${product.title} | Magazinul Nostru`,
description: product.description,
openGraph: {
title: product.title,
description: product.description,
images: [
{
url: `https://api.exemplu.ro/og?id=${product.id}`,
width: 1200,
height: 630,
},
],
},
};
}Am trecut recent un magazin online destul de stufos de pe Pages Router pe App Router în Next.js și m-am lovit direct de noul mod de a gestiona SEO. Dacă te-ai săturat de vechiul <Head> și vrei metadate dinamice care chiar funcționează fără workaround-uri dubioase, generateMetadata e răspunsul. Vreau să îți arăt cum am structurat noi treaba asta pentru a menține un scor SEO de 100% fără să ruinăm performanța.
Cum funcționează generateMetadata și capcana performanței
În App Router, nu mai injectăm tag-uri manual. Folosim funcția exportată generateMetadata pentru pagini dinamice. La un proiect cu peste 12.000 de produse, primul meu instinct a fost să mă îngrijorez: dacă facem fetch la detalii în generateMetadata și apoi facem iar fetch în componenta paginii, nu dublăm timpul de încărcare?
Aici intervine logica din Next.js. Framework-ul face deduplicare automată prin fetch. Dacă apelezi exact același endpoint cu aceiași parametri în ambele locuri, baza de date e interogată o singură dată. Am obținut un TTFB cu 120ms mai rapid după ce am curățat vechile apeluri redundante de API.
Totuși, există un trade-off destul de mare. Dacă API-ul tău extern răspunde greu, generateMetadata va bloca complet randarea paginii (server-side rendering). Utilizatorul va vedea un ecran alb mai mult timp. Soluția mea a fost să folosesc un timeout agresiv pe fetch-ul din metadate sau să am un fallback static pregătit.
Imaginile Open Graph dinamice pe bune
Generarea de imagini OG dinamice (cele care apar când dai share pe Slack sau Facebook) e adesea ignorată pentru că e greu de implementat. În Next.js, ai două variante mari. Ori folosești fișierul special opengraph-image.tsx în directorul rutei, ori definești URL-ul direct în generateMetadata.
Am testat ambele variante. Pentru pagini simple de blog, opengraph-image.tsx e excelent pentru că rulează @vercel/og sub capotă. Dar pentru e-commerce, unde aveam nevoie de branding diferit per categorie și watermark-uri dinamice pe pozele de produs, a fost mult mai simplu să generez URL-urile direct în obiectul de metadate, trimițând ID-ul produsului către un serviciu extern de procesare imagini.
Structured Data (JSON-LD) fără bătăi de cap
Aici e o chestie pe care mulți o greșesc. API-ul de metadate din Next.js nu gestionează JSON-LD în mod nativ direct în obiectul returnat. Cea mai curată metodă recomandată chiar și de echipa Next.js este să injectezi un tag <script> direct în componenta paginii tale.
Nu te complica să pui schema.org în metadate. Aruncă un script de tip application/ld+json direct în componenta de Server din page.tsx. React știe să facă hoist la script-urile de acest gen în head-ul documentului fără să strice hidratarea pe client.
Tu ce folosești pentru generarea imaginilor de share? Rămâi pe varianta nativă cu opengraph-image sau preferi un serviciu extern gen Cloudinary?