eduardweb.
DeploymentIntermediar#performance#seo#nextjs#metadata

Metadata API în Next.js: Cum faci SEO dinamic fără să distrugi performanța

De Dan Ciobanu, 2 iun. 2026 · 2 vizualizări · 3 like-uri

Postat 2 iun. 2026
typescript
import { cache } from 'react';
import { Metadata } from 'next';

// Cache manual pentru ORM-uri (non-fetch)
const getProduct = cache(async (id: string) => {
  const res = await db.select().from(products).where(eq(products.id, id));
  return res[0];
});

export async function generateMetadata({ params }: { params: { id: string } }): Promise<Metadata> {
  const product = await getProduct(params.id);
  if (!product) return {};

  return {
    title: `${product.title} | BrandName`,
    description: product.description,
    openGraph: {
      images: [{ url: product.imageUrl, width: 1200, height: 630 }],
    },
  };
}

export default async function Page({ params }: { params: { id: string } }) {
  const product = await getProduct(params.id);
  
  const jsonLd = {
    '@context': 'https://schema.org',
    '@type': 'Product',
    name: product.title,
    image: product.imageUrl,
    description: product.description,
  };

  return (
    <>
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      />
      <h1>{product.title}</h1>
    </>
  );
}

Salutare! Hai să vorbim direct pe problemă: cum facem SEO dinamic în Next.js fără să îngenunchem baza de date la fiecare request. Dacă ai pagini de produs sau articole generate dinamic, Metadata API e sfânt, dar vine cu niște capcane ascunse care îți pot distruge performanța dacă nu ești atent.

Capcana din generateMetadata și deduplicarea

Cea mai mare frică pe care am văzut-o la juniori (și sincer, am avut-o și eu la început) e dublarea fetch-urilor. Ai nevoie de titlul produsului în metadata, dar ai nevoie de el și în pagina propriu-zisă ca să-l randezi. Ce faci? Apelezi API-ul de două ori?

Răspunsul scurt este: da, dar Next.js (mai precis React) face deduplicare automată prin fetch. La un magazin cu peste 15.000 de produse active unde am implementat asta, am monitorizat query-urile și, într-adevăr, baza de date vede un singur request.

Totuși, dacă nu folosești fetch-ul nativ și ai un ORM sau un client de DB direct (cum e Prisma sau Drizzle), deduplicarea asta nu se mai întâmplă de la sine. Acolo trebuie să folosești React cache manual ca să înfășori funcția de fetch. Am pățit să dublez timpul de răspuns pe pagină doar pentru că am uitat să pun cache pe o interogare de Drizzle.

Cum facem cu Structured Data (JSON-LD)?

Aici e o dezbatere lungă pe forumuri. Unii încearcă să bage JSON-LD-ul tot în obiectul de metadata. Sfatul meu de senior trecut prin multe deploy-uri e să nu faci asta. E mult mai simplu și mai curat să injectezi JSON-LD direct în componenta de pagină, folosind un tag <script> clasic.

De ce? Pentru că e mult mai ușor de citit, TypeScript nu se plânge de tipuri dubioase în obiectul de metadata și poți folosi datele deja aduse în componentă fără să te mai complici cu alte apeluri. În plus, motoarele de căutare îl parsează perfect din body.

Trade-off-ul de performanță pe care mulți îl ignoră

generateMetadata este un proces blocant. Next.js nu va începe să trimită niciun octet de HTML către browser până când funcția ta generateMetadata nu s-a rezolvat complet. Dacă API-ul tău extern are o latență de 500ms, utilizatorul va vedea un ecran alb timp de jumătate de secundă, chiar dacă ai Suspense pus pe componentele din pagină.

Ca regulă de aur: în generateMetadata cere doar strictul necesar. Nu face un SELECT * pe tabelul de produse. Cere doar title, description și image_url. Am economisit cam 30% din timpul de Time to First Byte (TTFB) doar optimizând query-urile din metadata să nu mai aducă tot payload-ul de produs.

Voi cum gestionați metadatele dinamice în proiectele mari? Mergeți pe varianta cu script în pagină pentru JSON-LD sau ați găsit o metodă mai elegantă?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.