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

FunzionaNon funzionaAlternativa
flexbox, padding, border-radiusgrid e floatflex annidati
Stili inlineclassi Tailwindoggetto di stile esplicito
Immagini localifile letti a ogni richiesta senza cacheimport statico o data URL
Testo con un font caricatofont di sistema scelto per nomeArrayBuffer 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.