Skip to content

Dynamic Open Graph Images with Satori and Astro

Posted on:February 26, 2023

What are Open Graph Images?

Open Graph is a special type of meta tag that can be used to provide additional information about a page. It’s used by social media platforms like Facebook and Twitter to display a preview of a page when it’s shared. The most important tags are og:title, og:description and og:image. The first two are used to display the title and description of the page, while the latter is used to display an image. This image is called the Open Graph Image or OG Image.

Why should the OG Image be dynamic?

On most sites, the OG image is the same on all pages. This is a problem, because it means that the OG image is not representative of the page it’s shared from. However, it’s also possible to manually set the OG image for each page. This is a ton of work, easy to forget and hard to maintain (what if your design changes?).

A better solution is to generate the OG image dynamically for pages that follow the same structure (like articles). This way, the OG image is always representative of the page it’s shared from and the OG image can easily be updated in the future.

How to generate OG Images dynamically?

Your best bet is to use Satori to generate the OG images. It is a relatively new project from Vercel, but it’s already incredibly powerful and easy to use. In fact, it is just a single function call with a JSX template as the parameter.

Are there any alternatives to Satori?

Yes, there are a few alternatives to Satori, but they all have their own drawbacks. Most of the alternatives like Puppeteer require running a headless browser, which adds a ton of complexity and overhead. That’s why we will focus on Satori in this post.

How to install Satori?

You can just install Satori with your favorite package manager.

npm install satori
yarn add satori

How to use Satori?

Now, let’s use it to generate an OG image for this post.
First, we need to create a template for the OG image.

We can simply use good old JSX for this.

        background: "rgb(24, 32, 47)",
        width: "100%",
        height: "100%",
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
        color: "white",
        <h1 style={{ fontSize: "64px", fontWeight: "bold" }}>Hello World!</h1>

This is already enough to generate a basic OG image! The result of the above code is:

You can see a ton of production examples over at the Satori Playground.

To generate the code with Satori, you simply need to execute the following code.

import satori from "satori";

const svg = await satori(jsxHere, {
    // Add options here
    // For example to embed fonts or set a fixed width & height
    // View all options add

This code generates a string of SVG code.

You can either export the SVG code to a file or convert it to another format like PNG with an external library.

ReSVG-JS seems to be the most popular option for converting SVGs to PNGs. This should be fairly easy to implement with their documentation.

Unfortunately, I couldn’t get it to work with Astro due to this issue.

Vercel’s OG library does this automatically. However, it only works on Vercel.

How to implement Satori in your project

There are a range of methods to implement Satori in your project. I recommend creating an API route that generates the SVG code and then to return it as a string. Most static site generator should allow you to export a range of static paths that you can map to blog posts and their data. This way, you can let the site generator handle all the complicated functionality of storing SVGs.

How to implement Satori with Astro?

Create an API route in your Astro project by creating a file named [og].svg.ts in the src/pages directory.

Fill it with the following code.

import { getCollection } from "astro:content";
// This is the function that returns the SVG code
import generateOgImage from "@utils/generateOgImage";
import type { APIRoute } from "astro";

export const get: APIRoute = async ({ params }) => ({
  // Simply return all the SVG code as the body of the response
  body: await generateOgImage(params.ogTitle),

// Get all posts that are not drafts
const postImportResult = await getCollection("blog", ({ data }) => !data.draft);
const posts = Object.values(postImportResult);

// Export all posts as paths that can be mapped to the API route
export function getStaticPaths() {
  return posts
    .filter(({ data }) => !data.ogImage)
    .map(({ data }) => ({
      // Pass the title of the post so we can use it in the API route and as a static path
      params: { ogTitle: data.title },

Next, you can simply set the dynamic OG image path in the layout of your post by adding the following html.

<meta property="og:image" content={`${postTitleHere}`.svg} />

<!-- Optional: Set the width and height of the OG image -->
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />

How to implement Satori with webpack based static site generators like Nuxt 2?

With Webpack based static site generators like Nuxt 2, you can hook into the Webpack build process to generate OG Images and to add them to the dist folder. This requires more effort, but it allows you to simply add all necessary OG Images to the build.

General advice for implementing Satori with other tech stacks

You can also use serverless functions like Vercel’s, Netlify’s or AWS Lambda to generate the images on the fly. This allows you to generate OG images with an external service, which uncouples the OG image generation from your site. You can use the same code as the JSX template example above and embed the images anywhere with a direct link.

Further Reading