Copy link to headingCustomize a Vercel OG image with custom fonts
Every page on your site can have its own social card, rendered on request instead of designed by hand. @vercel/og builds that Open Graph (OG) image from JSX and CSS, and by default it returns plain text in a single bundled font. Customizing a Vercel OG image means changing the JSX you pass in, the options you set on ImageResponse, or both.
Every example below builds on the same route handler. Create app/api/og/route.tsx and paste the following code:
Run pnpm dev and browse to http://localhost:3000/api/og to see the rendered PNG.
Copy link to headingHow to use a custom font in your Vercel OG image
@vercel/og ships with one default font, so any other typeface has to be passed in as font data through the fonts option. You have two ways to supply it, depending on whether the font lives on Google Fonts or in your repository.
Copy link to headingLoad a font from Google Fonts at request time
Google Fonts can return a subset of a font containing only the characters you're rendering, which keeps the payload small. Fetch the stylesheet, extract the font file URL, and pass the result to ImageResponse:
The text parameter on the stylesheet request is what triggers subsetting, so pass the same string you're rendering. The name you give the font has to match the fontFamily value in your styles.
Copy link to headingLoad a font file from your repository
For a licensed font or one that isn't on Google Fonts, read the file from disk once at module scope so it isn't re-read on every request:
Bundled fonts count toward the 500KB bundle limit, which also covers your JSX, CSS, and images. A single weight of a subset font typically lands well under it.
Copy link to headingTroubleshoot failed to load font data errors
The Google Fonts helper throws failed to load font data when it can't return font data. Three causes account for nearly every instance:
- The family name doesn't match Google Fonts: The value passed to
loadGoogleFonthas to match the family name on Google Fonts, with+in place of spaces, as inPlayfair+Display. - The stylesheet returned woff2: The regex matches
opentypeandtruetypesources only, and@vercel/ogcan't parsewoff2in any case. Send the request exactly as written, because the format Google Fonts returns varies with the headers you send. - The font file request failed: The helper only returns data on a
200response, so a rate-limited or redirected request falls through to the error.
Log css before the regex runs to see which of the three you're hitting. The rest of the customization happens through the same ImageResponse options.
Copy link to headingHow to add emoji to an OG image
Emoji render as images rather than glyphs, so @vercel/og substitutes them from an emoji set that you choose with the emoji option. Set it alongside width and height:
The option is global for the image, so a single route renders one emoji style throughout.
Copy link to headingHow to use an external image URL in your Vercel OG image
Any absolute image URL can go in an <img> tag, which lets you build a card around content you don't control, such as a profile picture. Read the identifier from a query parameter and interpolate it into the src:
Set width and height on the <img> tag, which Satori recommends for embedded images. Handle the missing-parameter case as well, so a malformed link still returns a card instead of an error.
Copy link to headingHow to render non-Latin languages in an OG image
Non-Latin text renders from the same JSX as Latin text, with no fonts option required:
Right-to-left languages aren't supported by Satori, the layout engine underneath @vercel/og, so their text won't lay out correctly.
To render a script in a specific typeface rather than the default, pass the font through the fonts option. Whatever the script, the text itself changes per page, which is what URL parameters are for.
Copy link to headingHow to pass dynamic text to your Vercel OG image
A query parameter turns one route into a card for every page on your site. Read the parameter, cap its length, and fall back to a default when it's missing:
Reference it from your page metadata as /api/og?title=my%20post%20title, using an absolute URL so social platforms can fetch it. The slice(0, 100) guard matters, because an unbounded title overflows the card.
To stop anyone from generating cards with arbitrary text on your domain, see encrypting parameters.
Copy link to headingHow to show article headlines in social previews with Vercel OG
Rendering the headline inside the image keeps it readable in every link preview, regardless of how a social platform treats the page title. Combine a background image, a custom font, and a dynamic title in one route:
Swap the hardcoded values for a lookup against your content source, keyed on the article slug you pass in. The OG image template is a working version of this pattern if you'd rather start from a repository than build the route yourself.
Copy link to headingNext steps
With the customizations in place, the remaining work is deploying the route so social platforms can fetch the image at an absolute URL. Deploy the project this route lives in at vercel.com/new, or start from a preconfigured project in the template gallery.
Copy link to headingRelated resources
- OG Image Generation
- @vercel/og reference
- OG image examples
- Inspecting OG metadata
- Using an SVG image
- Using Tailwind CSS
- Encrypting parameters
Copy link to headingFrequently asked questions
Copy link to headingWhat size should a Vercel OG image be?
The recommended size is 1200x630 pixels, which is also the default width and height on ImageResponse, so both options can be omitted at that size. The values you pass become the pixel dimensions of the returned PNG, so keep them consistent with the og:image:width and og:image:height tags in your page markup.
Copy link to headingHow do I test an OG image before sharing the link?
Open the deployment in your Vercel dashboard and select the Open Graph tab, which renders previews for Twitter, Slack, Facebook, and LinkedIn from that deployment's OG metadata. For work on the image itself, the OG Playground renders your JSX in the browser, so you can check a layout without deploying.
Copy link to headingAre Vercel OG images cached?
Yes. @vercel/og sets cache-control: public, immutable, no-transform, max-age=31536000 on every response, so the CDN serves repeat requests without re-rendering. Because that header marks the response as immutable, change the URL, typically through a query parameter, when you want a new image for the same page.
Copy link to headingWhich font formats does @vercel/og support?
The library accepts ttf, otf, and woff font data, and ttf or otf parse faster than woff. woff2 isn't supported, so a font that renders in the browser can still fail in an OG image. Convert the file to ttf before passing it in the fonts array.