Open-graph image generation with Astro
Tags: astro, javascript, tutorial, react
Published: 2023-12-06 15:57:08

In this guide, I'll demonstrate how to automate the addition of generated open-graph images to your Astro websites, automating yet another tedious task.

# Open-graph Images

![Metatags example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6sagcb3zn7xf3odtqetr.png)

Here's a quick reminder about open-graph images. These are the images displayed when you share your link. They're easy to add—just insert the following `meta` tag into your HTML's `head`. However, while simple to add, they can be time-consuming to create and manage.

```html
<meta property="og:image" content="path-to-my-image.png" />
```

# Motivation

I recently shared my website template, [Launch](https://launch.jxd.dev), which I use for all my websites including [JXD](https://www.jxd.dev), [DocLabs](https://www.doclabs.dev), and [Alphabee](https://www.alphabee.dev). It automates many of the tedious, yet crucial tasks related to managing a website, allowing me to spend more time developing the products. Automating the creation of images across all websites significantly saves me time.

# Guide

Install the [`@vercel/og`](https://vercel.com/docs/functions/edge-functions/og-image-generation) package. This library is designed to convert React code into PNG images. It is built on [Satori](https://github.com/vercel/satori), a library that converts HTML and CSS into SVGs.

```bash
npm install @vercel/og
```

Create a new static resource endpoint for your images. In Launch, this is located at `src/pages/blog/og/[...slug].png.ts`. However, you can create it elsewhere, provided that it:

1. Resides in the `pages` directory
2. Includes the `...slug` path parameter
3. Has the `.png.ts` extension.

Add the following content:

```tsx
export const prerender = true;

import type { APIRoute } from "astro";
import { ImageResponse } from "@vercel/og";
import { createElement as el } from "react";
import { readFileSync } from "fs";

// Load your posts however you like
import { fetchPosts } from "@/lib/blog";

type Props = {
  title: string;
};

export const GET: APIRoute<Props> = ({ props }) => {
  const interBold = readFileSync("./src/fonts/Inter-Bold.ttf");

  return new ImageResponse(
    el(
      "div",
      {
        tw: "w-full h-full flex justify-center items-center bg-slate-50 text-5xl uppercase text-slate-800 font-bold",
        style: {
          fontFamily: "'Inter'",
        },
      },
      props.title
    ),
    {
      width: 1200,
      height: 630,
      fonts: [
        {
          name: "Inter",
          data: interBold,
          style: "normal",
          weight: 700,
        },
      ],
    }
  );
};

export async function getStaticPaths() {
  const posts = await fetchPosts();
  return posts.map((post) => ({
    params: { slug: post.slug },
    props: { title: post.data.title },
  }));
}
```

Let's take a deeper look at what is happening here.

First, we use `export const prerender = true`, which instructs Astro to build these images at build time. This is possible because the images don't change, and we deploy a new version each time a new post is published. However, if your content comes from a different source like a CMS, consider using an edge function and caching the results.

We're loading a font for use in our images that we previously downloaded and stored in our source code.

An `ImageResponse` is returned with our image content. This may appear unusual if you're accustomed to working with JSX. However, since Astro doesn't support `.jsx` or `.tsx` endpoints, we're using raw React with the `createElement` function. In our `div`, we use the `tw` attribute to specify which Tailwind classes to apply. While I'm a huge fan of Tailwind, you could opt for inline styles if that's your preference.

Finally, we're defining a `getStaticPaths` function to load our blog posts and pass the `slug` and `title` values.

We have now generated open graph images for each blog post. An example of this can be seen on Launch by visiting https://launch.jxd.dev/blog/og/hello-world.png.

Don't forget to add the `meta` tag to your blog post content so your images are included.

```html
<meta property="og:image" content={`/blog/og/${post.slug}.png`} />
```

# Conclusion

That concludes the process of adding generated open-graph images to an Astro website. If you're planning to launch a new website from scratch, consider checking out [Launch](https://launch.jxd.dev). It offers more than just open-graph images. It includes a functional blog, RSS feeds, a sitemap, robots.txt, analytics, and more.

Until next time ✌️