import { Metadata } from 'next';
import { cache } from 'react';
// Cache manual dacă nu folosești fetch-ul nativ cu cache
const getProduct = cache(async (id: string) => {
const res = await fetch(`https://api.exemplu.ro/products/${id}`);
return res.json();
});
interface Props {
params: Promise<{ id: string }>;
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { id } = await params;
const product = await getProduct(id);
return {
title: `${product.title} | Magazinul Meu`,
description: product.excerpt,
openGraph: {
title: product.title,
description: product.excerpt,
images: [
{
url: `https://api.exemplu.ro/og/${id}.png`,
width: 1200,
height: 630,
alt: product.title,
},
],
},
};
}
export default async function Page({ params }: Props) {
const { id } = await params;
const product = await getProduct(id);
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'Product',
name: product.title,
image: product.mainImage,
description: product.excerpt,
};
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<main>
<h1>{product.title}</h1>
<p>{product.description}</p>
</main>
</>
);
}Am migrat recent un magazin online destul de măricel, cam 14.000 de produse active, de la vechiul next/head la noul Metadata API din Next.js. Dacă pe proiecte mici te mai joci cu metadate statice în layout, la un catalog dinamic lucrurile devin rapid complicate. Vreau să vă arăt cum am structurat toată povestea asta ca să avem SEO impecabil, imagini de OpenGraph generate dinamic și date structurate JSON-LD, fără să facem praf timpul de încărcare al paginii.
Capcana fetch-ului dublu în generateMetadata
Cea mai mare frică a mea când am trecut la generateMetadata a fost că o să fac două request-uri la baza de date sau API pentru aceeași pagină: o dată ca să iau titlul și descrierea pentru SEO, și încă o dată în componenta principală Page ca să randezi efectiv produsul.
Din fericire, React și Next.js fac deduplicare automată pentru funcțiile de fetch native. Dacă apelezi același endpoint cu aceiași parametri în ambele locuri, request-ul se face o singură dată.
Dar atenție: asta funcționează doar dacă folosești fetch-ul nativ. Dacă folosești un ORM direct (gen Prisma sau Mongoose) sau un client de SDK care nu folosește fetch sub capotă, deduplicarea nu se întâmplă automat. În cazul ăsta, trebuie să folosești funcția cache din React pentru a memora rezultatul interogării în cadrul aceleiași randări.
Cum legăm JSON-LD pentru Google
O greșeală pe care o văd des este încercarea de a injecta JSON-LD-ul tot prin obiectul de metadata. Next.js nu are o cheie specială foarte flexibilă pentru asta în API-ul de metadate.
Cea mai curată metodă rămâne să randezi o etichetă <script> direct în interiorul componentei tale de pagină. Pur și simplu injectezi un obiect JSON parsat ca string. Google îl citește perfect, iar codul rămâne extrem de modular.
Trade-off-ul sincer: Performanță vs. Dinamism
Deși generateMetadata este extrem de curat pentru că rulează exclusiv pe server, vine cu un cost. Dacă API-ul tău extern are o latență de 500ms, acele 500ms se adaugă direct la TTFB-ul paginii (Time to First Byte). Serverul nu va trimite niciun octet de HTML către browser până când generateMetadata nu își termină execuția.
La proiectul nostru, am rezolvat asta parțial prin caching agresiv la nivel de API și prin utilizarea generateStaticParams pentru produsele cele mai vândute (top 2000), lăsând restul pe randare dinamică la cerere. Pentru paginile randate pur dinamic, asigură-te că query-ul de metadata ia doar câmpurile esențiale (titlu, descriere, imagine), nu tot obiectul de produs cu toate variantele și stocurile lui.
Cum gestionați voi imaginile de OG? Generare dinamică cu @vercel/og sau le generați static la salvarea produsului în CMS?