import { cache } from 'react';
import { Metadata } from 'next';
// React cache previne interogarea dublă în DB la apeluri simultane pe același request
const getProduct = cache(async (slug: string) => {
// Aici faci interogarea reală în baza de date
return {
title: 'Monitor Gaming UltraWide',
description: 'Cel mai bun monitor pentru developeri',
slug
};
});
type Props = {
params: Promise<{ slug: string }>;
};
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug } = await params;
const product = await getProduct(slug);
return {
title: `${product.title} | Magazinul Meu`,
description: product.description,
openGraph: {
title: product.title,
description: product.description,
images: [
{
url: `/produse/${product.slug}/opengraph-image`,
width: 1200,
height: 630,
},
],
},
};
}Hai să vorbim pe șleau despre SEO în Next.js App Router, că văd des confuzii pe forum despre cum se leagă dinamic metadatele. Am trecut recent un magazin online cu peste 12.000 de produse de la vechiul Pages Router la Next.js App Router și am refăcut toată logica de SEO. Schimbarea de la vechiul next/head la noul Metadata API e masivă, dar vine cu niște chichițe de performanță pe care e bine să le știi înainte să-ți bubuie serverul în producție.
La proiectul menționat, am reușit să scădem timpul de indexare în Google Search Console cu aproape 4 zile pentru paginile noi, doar pentru că am structurat corect metadatele și am servit JSON-LD-ul corect din prima secundă, direct pe server.
generateMetadata și capcana performanței
Dacă ai pagini dinamice (de exemplu, o pagină de produs /produs/[slug]), Next.js te lasă să exporți o funcție asincronă generateMetadata. Sună extrem de simplu, dar ai grijă mare la cum faci fetch-urile de date.
Implicit, dacă faci un apel API în generateMetadata și apoi faci exact același apel în componenta paginii tale, Next.js se prinde și face deduplicare (folosind un cache global pe fetch-ul nativ). Trade-off-ul? Asta funcționează DOAR dacă folosești fetch-ul nativ din Node/Next. Dacă folosești un ORM ca Prisma, Kysely sau un client direct de Postgres/MySQL (cum am avut eu pe proiect), deduplicarea automată nu funcționează. Te trezești că baza ta de date e lovită de două ori pentru fiecare accesare de pagină.
Soluția este să folosești utilitarul cache de la React. Împachetezi funcția care aduce datele din DB în cache, iar React se va asigura că interogarea rulează o singură dată pe request.
Imagini Open Graph (OG) dinamice
Pentru rețelele sociale, o imagine OG dinamică (care conține titlul produsului, prețul și un background mișto) crește rata de click cu peste 40%. Next.js îți permite să generezi aceste imagini folosind fișiere speciale opengraph-image.tsx poziționate direct în folderul rutei.
Sub capotă, Next folosește Satori ca să transforme HTML/CSS în SVG și apoi în PNG. Merge brici pentru design-uri simple, dar e destul de limitat. Nu poți folosi toate proprietățile de CSS, iar fonturile personalizate trebuie încărcate manual ca ArrayBuffer. Dacă ai nevoie de un layout complex cu umbre ciudate sau efecte de glassmorphism, Satori o să-ți dea bătăi de cap. Pentru chestii simple însă, e ideal pentru că rulează la Edge și e super rapid.
Structura de JSON-LD (Date Structurate)
Google iubește datele structurate, mai ales pentru produse, review-uri și FAQ. Greșeala pe care o văd des e încercarea de a pune JSON-LD-ul direct în obiectul de metadate returnat de generateMetadata. Next.js nu are o proprietate nativă curată pentru asta în obiectul de metadata.
Cea mai bună metodă este să injectezi schema JSON-LD direct în componenta paginii, folosind un tag <script> cu dangerouslySetInnerHTML. Nu te speria, este perfect sigur atâta timp cât datele sunt deja sanitizate de API-ul tău.
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'Product',
name: product.title,
image: product.image,
description: product.description,
};
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<main>...</main>
</>
);
Prin abordarea asta, crawlerul Google citește datele direct din payload-ul HTML inițial, fără să mai aștepte hidratarea de client side React. Pe noi asta ne-a ajutat enorm să obținem rich snippets în paginile de rezultate Google.
Tu cum gestionezi metadatele dinamice în proiectele mari? Mergi pe varianta nativă din Next sau preferi să folosești tool-uri externe?