eduardweb.
DeploymentIntermediar#seo#nextjs#typescript#app-router#metadata

Metadata API în Next.js 16: Dynamic SEO, OG Images și JSON-LD fără bătăi de cap

De Diana Oprea, 5 aug. 2026 · 6 vizualizări · 2 like-uri

Postat 5 aug. 2026
typescript
import { cache } from 'react';
import type { Metadata } from 'next';
import { db } from '@/lib/db';

// Deduplicăm query-ul de DB între generateMetadata și Page
const getProduct = cache(async (id: string) => {
  return await db.products.findUnique({ where: { id } });
});

type Props = {
  params: Promise<{ id: string }>;
};

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const { id } = await params;
  const product = await getProduct(id);

  if (!product) return { title: 'Produsul nu a fost găsit' };

  return {
    title: `${product.name} | Magazinul Nostru`,
    description: product.shortDescription,
    openGraph: {
      title: product.name,
      images: [`/api/og?title=${encodeURIComponent(product.name)}&price=${product.price}`],
    },
  };
}

Dacă încă mai pui <head> manual sau te chinui cu pachete terțe pentru SEO în App Router, pierzi timpul de pomană. În Next.js 16, API-ul de Metadata e atât de bine integrat încât rezolvă SEO-ul dinamic, OpenGraph și JSON-LD în câteva linii, fără să îți strice TTFB-ul dacă știi unde să pui caching.

Am refăcut recent SEO-ul pentru un marketplace cu peste 45.000 de produse active. Trecerea pe generateMetadata ne-a scăzut drastic bug-urile de pre-rendering și am urcat la 100/100 pe Lighthouse la capitolul SEO. Dar nu a fost totul roz de la început – am dat peste două capcane serioase de performanță.

Cum funcționează generateMetadata și unde se rupe filmul

Funcția generateMetadata rulează exclusiv pe server și primește params și searchParams (nu uita că în Next.js 15 și 16 acestea sunt Promise-uri și trebuie să le dai await). Marele avantaj e că Next.js deduce automat deduplicarea cererilor fetch() făcute în generateMetadata și în componenta de pagină. Adică dacă apelezi un API extern în ambele locuri, Next execută un singur HTTP request.

Atenție însă: dacă folosești un ORM sau client direct de bază de date (Prisma, Drizzle, Kysely), deduplicarea automată din fetch NU mai funcționează! La proiectul de 45k produse, am observat că timpul de query s-a dublat la fiecare afișare, pentru că baza de date era interogată o dată pentru titlu și descriere și încă o dată pentru conținutul paginii.

Rezolvarea e simplă: înfășori apelul de DB în cache() din React (import { cache } from 'react'). Astfel, query-ul rulează o singură dată per request render cycle. Dacă uiți de asta, latența paginii crește considerabil.

Dynamic OpenGraph Images: Merită efortul?

A doua chestie unde am ezitat a fost generarea dinamică de imagini pentru social media folosind @vercel/og și ImageResponse.

Aici există un trade-off clar:

  • Avantaj: Rata de click (CTR) pe rețelele sociale a crescut cu 22% când am inclus prețul, titlul și stocul direct pe imaginea generată dinamic.
  • Dezavantaj: Render-ul pe Edge Runtime adaugă un overhead de 50-100ms la prima cerere dacă imaginea nu e servită direct din CDN sau dacă nu ai o strategie agresivă de revalidate.

Dacă ai un site mic (sub 1.000 de pagini), generează imaginile la build sau folosește imagini statice. Dacă ai zeci de mii de pagini dinamice, folosește ImageResponse din next/og, dar asigură-te că pui un header agresiv de Cache-Control în Endpoint-ul de API.

Injectarea curată de JSON-LD (Structured Data)

Pentru Google Rich Snippets, schema JSON-LD este esențială. Din păcate, obiectul Metadata din Next.js nu oferă o proprietate nativă jsonLd direct în tipuri.

Soluția curată și recomandată de echipa Vercel este să creezi o componentă simplă de server care injectează un tag <script type="application/ld+json">. Nu te speria de dangerouslySetInnerHTML aici – dacă validezi datele cu Zod sau vin direct dintr-o sursă sigură, e abordarea corectă și nu afectează hidratarea React.

Voi ce strategie folosiți pentru imaginile OG la volume mari? Rămâneți pe @vercel/og sau preferați un microserviciu separat cu Puppeteer?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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