React
OG image generata nell'App Router
Un file opengraph-image.tsx accanto alla rotta produce il PNG con ImageResponse. Cosa supporta Satori, i font da caricare e il doppione.
In sintesi
- opengraph-image.tsx accanto alla pagina copre quella rotta e le figlie.
- ImageResponse usa Satori: flexbox e stili inline, niente grid né classi Tailwind.
- I font vanno passati come ArrayBuffer, o il testo esce in un carattere non scelto.
- Il file convention vince su openGraph.images: non dichiarare l'immagine due volte.
Un'immagine Open Graph generata è un file opengraph-image.tsx accanto alla rotta: Next.js lo esegue e restituisce un PNG. Il nome è una convenzione, non una scelta, e il file vale per la pagina e per le rotte figlie.
Il file minimo
// app/blog/[slug]/opengraph-image.tsx
import { ImageResponse } from "next/og";
export const size = { width: 1200, height: 630 };
export const contentType = "image/png";
export default async function Image({ params }: { params: Promise }) {
const { slug } = await params;
return new ImageResponse(
(
ui.eneawork.it/blog
{slug.replace(/-/g, " ")}
),
size,
);
}
Il file esporta size, contentType e un componente async. Se legge params o una fetch non in cache, l'immagine viene generata a ogni richiesta; altrimenti Next.js la produce una volta in build.
Cosa Satori non fa
| Funziona | Non funziona | Alternativa |
|---|---|---|
| flexbox, padding, border-radius | grid e float | flex annidati |
| Stili inline | classi Tailwind | oggetto di stile esplicito |
| Immagini locali | file letti a ogni richiesta senza cache | import statico o data URL |
| Testo con un font caricato | font di sistema scelto per nome | ArrayBuffer letto dal file |
Dove entra il font
// inside the function that renders the image
const font = await readFile(
join(process.cwd(), "public/fonts/inter-semibold.ttf"),
);
return new ImageResponse(
Titolo,
{
...size,
fonts: [{ name: "Inter", data: font, weight: 600, style: "normal" }],
},
);
Senza fonts il testo esce in un carattere che non hai scelto, e i pesi non corrispondono al sito. Carica il file una volta sola e riusalo in tutte le funzioni immagine.
Il doppione da evitare
Se dichiari openGraph.images in generateMetadata e lasci anche opengraph-image.tsx, la convenzione ha la precedenza e la tua immagine sparisce. Scegli una strada: file per le immagini generate, metadata per quelle statiche.
I pezzi della scena arrivano dal catalogo: hero, badge e tipografia si ricompongono in stili inline.
I metadata dinamici sono in metadata dinamica, il titolo in come scrivere un title tag e la descrizione in meta description che funziona. Confronto: Astro vs Next.js.
curl -sI http://localhost:3000/blog/hello/opengraph-image
Limite noto: il risultato è un PNG raster. Il testo sotto i 24 px si legge male su LinkedIn e la generazione consuma CPU: se la scena è sempre identica, un file statico è più veloce.
Domande frequenti
Dove va il file opengraph-image?
Nella cartella della rotta, accanto a page.tsx. Vale per quella pagina e per le rotte figlie che non ne dichiarano una propria.
Posso usare Tailwind dentro ImageResponse?
No. Satori accetta stili inline: converti i token in un oggetto JavaScript e passa quello.
Perché il font esce sbagliato?
Perché non hai passato il font. ImageResponse non legge i font del sito: carica il file come ArrayBuffer e dichiaralo nell'opzione fonts.
L'immagine viene generata a ogni richiesta?
Solo se legge dati dinamici come params o una fetch non in cache. Con contenuto statico Next.js la genera una volta in build.
Serve anche twitter-image?
Solo se vuoi un'immagine diversa da quella Open Graph. Se il formato va bene, Twitter usa la stessa immagine.