TechnologieWeb

Automatisch generierte Open-Graph-Bilder für Artikel-Websites

netcomputing.de Redaktion26. August 202610 Min. Lesezeit

Zusammenfassung

Ein Magazin mit laufend neuen Artikeln braucht für jeden ein eigenes Vorschaubild für Slack, X, LinkedIn und WhatsApp, denn ein Link ohne Bild wird seltener angeklickt. Manuelles Design pro Artikel skaliert bei Dutzenden Texten im Monat nicht. Next.js löst das über die Dateikonvention opengraph-image.tsx und die Funktion ImageResponse aus next/og: Der Artikeltitel wird als JSX gerendert und automatisch zu einem PNG mit Meta-Tags. So entsteht je Artikel ein eigenes Bild ohne Design-Tool.

Eine Website, die einen einzelnen Artikel veröffentlicht, kann sich ein von Hand gestaltetes Vorschaubild leisten. Ein Magazin oder ein Portfolio aus mehreren Websites, das laufend neue Artikel, Produktseiten oder Ratgeber hinzufügt, kann das nicht mehr: Bei zwanzig oder dreißig neuen Texten im Monat müsste jedes einzelne Bild in einem Design-Werkzeug angelegt, mit dem Titel beschriftet und exportiert werden, bevor der Artikel online geht. Ohne dieses Bild zeigt ein geteilter Link in Slack, X, LinkedIn oder WhatsApp entweder gar kein Bild oder ein generisches Website-Logo, und ein Link ohne erkennbares Vorschaubild wird nachweislich seltener angeklickt als einer mit einem passenden Bild.

Next.js löst dieses Problem im App Router, dem seit Version 13 aktuellen Routing-System, mit einer eigenen Dateikonvention: einer Datei namens opengraph-image.tsx, die statt eines fertigen Bildes eine Funktion exportiert. Diese Funktion baut das Bild bei jedem Build oder bei jeder Anfrage aus echten Daten zusammen, etwa aus dem Titel und der Kategorie eines Artikels, und übergibt das Ergebnis an die Funktion ImageResponse aus dem Paket next/og. Das Bild entsteht damit aus JSX und CSS, denselben Werkzeugen, mit denen auch der Rest der Seite gebaut wird, und nicht aus einer separaten Bildbearbeitung.

Systemvoraussetzungen

Die automatische Bildgenerierung braucht wenige, aber genau passende Bausteine. Die folgende Tabelle nennt die aktuellen Anforderungen und wofür jeder Baustein steht.

KomponenteVersion oder AnforderungZweck
Next.jsab Version 13.3 (opengraph-image eingeführt), empfohlen Version 16.x im App RouterStellt die Dateikonvention opengraph-image.tsx und die Funktion ImageResponse über next/og bereit
Node.jsmindestens Version 20.9 LTS, Pflicht seit Next.js 16Laufzeitumgebung für Build und Bildgenerierung
TypeScriptmindestens Version 5.1, Next.js-16-MindestanforderungTypsicherheit für die async-Parameter der Bildfunktion
DatenquelleJSON-Datei, Datenbank oder Headless-CMS mit Titel und Kategorie je ArtikelLiefert die Werte, die in das Bild gerendert werden
SchriftdateiFormat TTF, OTF oder WOFF (WOFF2 wird nicht unterstützt)Eigene Markenschrift statt der Systemschrift im generierten Bild
Bundle-Budgetmaximal 500 KB für JSX, CSS, Schriftdateien und eingebettete Bilder zusammenHarte Obergrenze der Rendering-Engine hinter ImageResponse

Systemarchitektur

Eine Next.js-Website mit automatisch erzeugten Vorschaubildern hat zwei Bausteine, die zusammenspielen: die Datenquelle, aus der Titel und Kategorie eines Artikels stammen, und die Datei opengraph-image.tsx, die diese Werte bei jedem Build oder jeder Anfrage in ein PNG-Bild übersetzt. Ein dritter Baustein kommt erst ins Spiel, wenn der Link geteilt wird: der Bild-Cache der jeweiligen Plattform. Das folgende Schaubild zeigt den Weg von der Veröffentlichung eines Artikels bis zur Vorschau in einem Chat oder sozialen Netzwerk.

flowchart TD
    A[Redaktion veroeffentlicht oder aendert einen Artikel] --> B[Next.js wertet opengraph-image.tsx fuer die Artikelroute aus]
    B --> C[Titel und Kategorie werden aus der Datenquelle gelesen]
    C --> D[JSX-Baum mit Titel Kategorie und Markenfarben entsteht]
    D --> E[ImageResponse rendert das JSX ueber Satori zu einem SVG]
    E --> F[Resvg wandelt das SVG in ein PNG um]
    F --> G[Next.js traegt og:image und twitter:image Meta-Tags automatisch ein]
    G --> H[Jemand teilt den Artikel-Link in Slack X LinkedIn oder WhatsApp]
    H --> I[Die Plattform ruft die Seite ab und liest die Meta-Tags]
    I --> J[Die Plattform speichert das PNG in ihrem eigenen Bild-Cache]
    J --> K[Eine spaetere Titelaenderung zeigt sich dort zunaechst nicht]
    K --> L[Sharing Debugger oder Post Inspector erzwingt einen erneuten Abruf]

Beim Build oder bei einer Anfrage ruft Next.js die Standardfunktion aus opengraph-image.tsx auf und übergibt ihr, sofern die Datei in einer dynamischen Route liegt, die Routenparameter als Promise. Die Funktion liest daraus den Artikel-Slug, holt Titel und Kategorie aus der Datenquelle und übergibt beides als JSX an ImageResponse. Intern setzt next/og dafür zwei Werkzeuge ein: Satori übersetzt das JSX und die zugehörigen CSS-Eigenschaften zunächst in ein SVG, und Resvg wandelt dieses SVG anschließend in das eigentliche PNG um. Next.js liest dabei die exportierten Werte alt, size und contentType aus derselben Datei und trägt daraus automatisch die passenden Meta-Tags im Kopfbereich der Seite ein, ohne dass eine zusätzliche Angabe in generateMetadata nötig wäre. Was danach mit dem Bild passiert, liegt außerhalb der eigenen Website: Jede Plattform, die den Link abruft, speichert das gefundene Bild in ihrem eigenen, plattformspezifischen Cache, und genau dieser Cache ist beim letzten Schritt im Schaubild die eigentliche Fehlerquelle.

Schritt-für-Schritt-Umsetzung

Opengraph-image-Datei für die Artikelroute anlegen

Die Datei opengraph-image.tsx liegt direkt in dem Ordner, dessen Seiten sie mit einem Bild versorgen soll, genau neben der zugehörigen page.tsx. Bei einer Website mit der Routenstruktur app/[slug]/page.tsx, wie sie auch netcomputing.de für einzelne Artikel verwendet, liegt die Bilddatei also unter app/[slug]/opengraph-image.tsx und erhält denselben Routenparameter wie die Seite selbst. Seit Next.js 16 ist dieser Parameter, ebenso wie bei einer normalen Seite, ein Promise, das erst mit await aufgelöst werden muss. Die Datei exportiert zusätzlich drei einfache Konstanten: alt für den Alternativtext, size für Breite und Höhe des Bildes, und contentType für den MIME-Typ der Ausgabe.

Titel und Marke aus der Datenquelle rendern

Der eigentliche Bildinhalt entsteht als JSX, das an ImageResponse übergeben wird. Die Rendering-Engine hinter next/og, Satori, behandelt dabei jedes Element standardmäßig als Flexbox-Container und kennt kein display: grid; ein Layout, das im Browser über CSS Grid gebaut würde, muss hier über verschachtelte Flexbox-Container nachgebaut werden. Das folgende Beispiel liest Titel und Kategorie eines Artikels aus der Datenquelle, lädt eine eigene Schriftdatei aus dem Dateisystem und rendert beides in ein Bild im Format 1200 mal 630 Pixel, der Standardgröße, die ImageResponse ohne weitere Angabe ohnehin voreinstellt.

import { ImageResponse } from 'next/og';
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { getArticleBySlug } from '@/lib/articles';

export const alt = 'Artikel-Vorschaubild';
export const size = { width: 1200, height: 630 };
export const contentType = 'image/png';

const interBold = await readFile(
  join(process.cwd(), 'assets/fonts/Inter-Bold.ttf')
);

export default async function Image({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  const article = await getArticleBySlug(slug);
  const title = article?.title ?? 'netcomputing.de';
  const category = article?.categories?.[0] ?? 'Technologie';

  return new ImageResponse(
    (
      <div
        style={{
          display: 'flex',
          flexDirection: 'column',
          justifyContent: 'space-between',
          width: '100%',
          height: '100%',
          padding: '64px',
          backgroundColor: '#0f172a',
          color: '#f8fafc',
        }}
      >
        <div
          style={{
            display: 'flex',
            fontSize: 28,
            textTransform: 'uppercase',
            letterSpacing: 4,
            color: '#38bdf8',
          }}
        >
          {category}
        </div>
        <div
          style={{
            display: 'block',
            fontSize: titleFontSize(title),
            fontWeight: 700,
            lineHeight: 1.25,
          }}
        >
          {title}
        </div>
        <div style={{ display: 'flex', fontSize: 24, color: '#94a3b8' }}>
          netcomputing.de
        </div>
      </div>
    ),
    {
      ...size,
      fonts: [{ name: 'Inter', data: interBold, weight: 700, style: 'normal' }],
    }
  );
}

function titleFontSize(title: string) {
  if (title.length > 90) return 42;
  if (title.length > 60) return 52;
  return 64;
}

Jede Schriftdatei wird einmal beim Start des Moduls eingelesen, nicht bei jedem Aufruf der Funktion Image, weil sie sich zwischen zwei Anfragen nicht ändert. Next.js behandelt eine so erzeugte Bilddatei standardmäßig wie eine statisch optimierte Seite: Sie entsteht einmal beim Build und wird danach ausgeliefert, solange keine Request-time-Funktion wie cookies oder headers und keine abweichende Cache-Einstellung im Spiel ist. Wer stattdessen bei jeder Anfrage ein taufrisches Bild erzeugen will, etwa weil sich der Artikeltitel zwischen zwei Deployments ändert, setzt in derselben Datei die Route-Segment-Option export const dynamic = 'force-dynamic'.

Lange Artikeltitel ohne Bildfehler darstellen

Ein Open-Graph-Bild hat für den Titel nur eine feste Fläche, Artikeltitel schwanken aber stark in der Länge. Anders als ein Browser bricht next/og einen zu langen Text nicht automatisch passend um oder skaliert ihn nach; ohne Gegenmaßnahme läuft ein langer Titel entweder über den Bildrand hinaus oder wird von der festen Bildhöhe stumm abgeschnitten. Die Funktion titleFontSize im Beispiel oben löst das über eine einfache Regel: Ein Titel unter 60 Zeichen erscheint groß, ein mittellanger Titel etwas kleiner, und ein Titel über 90 Zeichen bekommt die kleinste der drei Stufen, ohne dass ein Wort verloren geht. Wo stattdessen eine echte Kürzung gewünscht ist, unterstützt Satori auch eine mehrzeilige Kürzung mit Auslassungspunkten über die CSS-Eigenschaft WebkitLineClamp auf einem Container mit display: '-webkit-box', ähnlich wie im Browser üblich. Das einfachere text-overflow: ellipsis auf einer einzelnen Zeile funktioniert in Satori dagegen nur, wenn der Textcontainer ausdrücklich display: 'block' trägt, weil jedes Element hier ohne eigene Angabe als Flexbox behandelt wird und nicht, wie im Browser gewohnt, als Block-Element.

Häufige Fehlerquellen

  • Ein Layout mit display: grid bleibt leer oder verrutscht. Satori, die Rendering-Engine hinter next/og, unterstützt laut eigener Dokumentation nur Flexbox und eine Teilmenge von CSS, CSS Grid gehört ausdrücklich nicht dazu. Das Layout muss stattdessen mit verschachtelten Flexbox-Containern gebaut werden.
  • Eine WOFF2-Schriftdatei führt zur Systemschrift statt zur Markenschrift. next/og unterstützt laut Next.js-Dokumentation nur die Formate TTF, OTF und WOFF. Eine WOFF2-Datei wird nicht mit einer Fehlermeldung abgelehnt, das Bild erscheint einfach in einer anderen Schrift als erwartet. Die Schriftdatei muss vorher in TTF oder OTF konvertiert werden.
  • Text zentriert oder umbricht nicht wie erwartet. Jedes Element wird in Satori standardmäßig als Flexbox behandelt, ein <div> verhält sich hier also anders als im Browser, wo es ohne Angabe ein Block-Element wäre. Jeder Container, der zentrieren oder ausrichten soll, braucht deshalb ein eigenes display: 'flex'.
  • Die Bildgenerierung bricht bei großen eingebetteten Assets ab. ImageResponse begrenzt laut Next.js-Dokumentation die gesamte Bundle-Größe aus JSX, CSS, Schriftdateien und eingebetteten Bildern auf 500 KB. Ein hochauflösendes Logo als Base64-Daten-URL sprengt dieses Budget schnell; große Assets sollten stattdessen als kleinere Datei eingebunden oder zur Laufzeit nachgeladen werden.
  • Vergessenes await bei den Routenparametern. Seit Next.js 16 erhält die Bildfunktion params als Promise statt als einfaches Objekt. Ein direkter Zugriff wie params.slug ohne vorheriges await liefert kein Ergebnis mehr und muss auf await params umgestellt werden.
  • Ein korrigiertes Bild erscheint beim Teilen trotzdem noch alt. Facebook, LinkedIn und andere Plattformen speichern das Vorschaubild einer URL in einem eigenen Cache und rufen es nicht bei jedem neuen Teilen erneut ab. Erst ein erzwungener Neuabruf über ein plattformeigenes Werkzeug wie den Sharing Debugger oder den Post Inspector zeigt die aktuelle Version.

Häufig gestellte Fragen

Was ist die Next.js-Dateikonvention opengraph-image?

Die Dateikonvention opengraph-image ist ein fest benannter Dateiname, den Next.js im App Router jedem Routenabschnitt zuordnet, um dessen Vorschaubild für soziale Netzwerke festzulegen. Als Bilddatei (.jpg, .png oder .gif) liefert sie ein fertiges Motiv, als .tsx-Datei exportiert sie stattdessen eine Funktion, die das Bild mit ImageResponse aus next/og bei jedem Build oder jeder Anfrage neu erzeugt. Next.js liest die Datei automatisch aus und baut daraus die passenden Meta-Tags im Kopfbereich der Seite.

Muss das Open-Graph-Bild zusätzlich über generateMetadata eingetragen werden?

Nein, ein über die Dateikonvention angelegtes Open-Graph-Bild muss nicht zusätzlich über generateMetadata eingetragen werden. Next.js wertet die Datei opengraph-image aus und trägt daraus die Tags og:image, og:image:width, og:image:height und, sofern die Konstante alt exportiert wurde, auch og:image:alt automatisch in den Kopfbereich jeder betroffenen Seite ein. Ein manuell gesetztes openGraph.images-Feld in generateMetadata ist für diesen Anwendungsfall überflüssig, weil die Dateikonvention denselben Tag bereits selbstständig erzeugt.

Warum unterstützt next/og kein CSS Grid?

next/og unterstützt kein CSS Grid, weil die zugrunde liegende Rendering-Engine Satori JSX und CSS nicht wie ein Browser, sondern über eine eigene, auf Flexbox beschränkte Layout-Engine in ein SVG übersetzt. Laut der Dokumentation von Next.js sind nur Flexbox und eine Teilmenge weiterer CSS-Eigenschaften nutzbar, fortgeschrittene Layouts wie display: grid funktionieren ausdrücklich nicht. Ein Raster aus mehreren Spalten und Zeilen muss deshalb über verschachtelte Flexbox-Container mit flexDirection: 'row' und 'column' nachgebaut werden.

Ein geteilter Link zeigt nach einer Artikeländerung deshalb noch das alte Vorschaubild, weil Plattformen wie Facebook, LinkedIn oder WhatsApp das einmal abgerufene Bild in einem eigenen, von der eigenen Website unabhängigen Cache ablegen und es nicht bei jedem erneuten Teilen automatisch neu abrufen. Facebooks eigene Entwicklerdokumentation beschreibt für genau diesen Fall den Sharing Debugger: Eine URL, dort eingetragen, löst laut Facebook einen erneuten Abruf der Seite aus und aktualisiert damit die zwischengespeicherten Open-Graph-Angaben für diese eine Adresse. LinkedIn stellt mit dem Post Inspector ein vergleichbares Werkzeug für die eigene Plattform bereit. Für ein Portfolio mit vielen Artikeln lohnt sich diese Prüfung vor allem nach einem größeren Layout-Wechsel des Bildes, nicht nach jeder einzelnen Textänderung.

Quellen

  1. opengraph-image and twitter-image (Dateikonvention, automatische Meta-Tags), Next.js Dokumentation (Vercel), abgerufen am 2026-08-26
  2. ImageResponse (Funktionsreferenz, Optionen, Bundle-Grenze, Schriftformate), Next.js Dokumentation (Vercel), abgerufen am 2026-08-26
  3. Upgrading to Version 16 (Node.js- und TypeScript-Mindestversionen, async params bei opengraph-image), Next.js Dokumentation (Vercel), abgerufen am 2026-08-26
  4. satori README (unterstützte CSS-Eigenschaften, display-Werte, text-overflow und lineClamp), Vercel / GitHub (vercel/satori), abgerufen am 2026-08-26
  5. Sharing Webmasters Guide (Sharing Debugger und Cache-Verhalten von Vorschaubildern), Meta for Developers, abgerufen am 2026-08-26
  6. Sharing Debugger (Werkzeug zum erneuten Abruf einer geteilten Seite), Meta for Developers, abgerufen am 2026-08-26
  7. Post Inspector (Werkzeug zur Prüfung der Link-Vorschau), LinkedIn, abgerufen am 2026-08-26

Der Content wurde mit Hilfe von KI erstellt, vor allem in der Recherche und Vorformulierung. Prüfung und Abnahme durch unsere Redaktion.