How to use `astro-og-canvas` with Astro

By Aidan Kinzett

What is an Open Graph Image?

Open Graph images are the images that are displayed when you share a link to your blog post on social media. Open Graph is a standard that lets you provide details about your website through meta tags in your HTML. You can read more about the Open Graph Protocol here. The main thing to know is that you set the image by adding a meta tag to the head of your page:

<meta property="og:image" content="" />

If you inspect the source code of this page, you will see that the og:image tag is set to

How to Generate Open Graph Images

We will be using the astro-og-canvas package to generate our images.

  1. First, install the package:
npm install astro-og-canvas
  1. Create a new file in your src/pages directory. For example, src/pages/og/[...route].ts. This will be the route that generates the Open Graph images.

  2. Add the following code to the file:

// src/pages/og/[...route].ts
import { OGImageRoute } from "astro-og-canvas";

const directory = "src/content";

// Import all pages from the content directory
const rawPages = import.meta.glob(`${directory}/**/*.md`, { eager: true });

// Remove the /src/content prefix from the paths
const pages = Object.entries(rawPages).reduce(
  (acc, [path, page]) => ({ ...acc, [path.replace(directory, "")]: page }),

export const { getStaticPaths, get } = OGImageRoute({
  // Set the name of the dynamic route segment here it’s `route`,
  // because the file is named `[...route].ts`.
  param: "route",

  // Provide our pages object here

  // For each page, this callback will be used to
  // customize the OpenGraph image.
  getImageOptions: (path, page) => ({
    title: page.frontmatter.title,
    description: page.frontmatter.description,

Set directory to the path of your content directory. In this example, we are using src/content.

There are more options you can provide in the getImageOptions function to customize the Open Graph image. You can read more about them in the astro-og-canvas documentation.

  1. Create a helper function to add the Open Graph image to the data of each page:
// src/lib/getBlogCollection.ts
import { getCollection } from "astro:content";
import { SITE_URL } from "../consts";

const collection = "blog";

export default async () => {
  const posts = await getCollection(collection);

  return => ({,
    data: {,
      ogImage: `${SITE_URL}/og/${collection}/${post.slug}.png`,
  1. Call the helper function to get the blog posts in your src/pages/blog/[...slug].ts file instead of using getCollection:
// src/pages/blog/[...slug].ts
export async function getStaticPaths() {
  const posts = await getBlogCollection();
  return => ({
    params: { slug: post.slug },
    props: post,
  1. Add the Open Graph image to the BaseHead.astro component:
// src/components/BaseHead.astro
// Import the global.css file here so that it is included on
// all pages through the use of the <BaseHead /> component.
import "../styles/global.css";

export interface Props {
  title: string;
  description: string;
  ogImage?: string;

const canonicalURL = new URL(Astro.url.pathname,;

const { title, description, ogImage = "/default-og.png" } = Astro.props;

<!-- Global Metadata -->
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="generator" content={Astro.generator} />

<!-- Canonical URL -->
<link rel="canonical" href={canonicalURL} />

<!-- Primary Meta Tags -->
<meta name="title" content={title} />
<meta name="description" content={description} />

<!-- Open Graph Tags -->
<meta property="og:title" content={title} />
<meta property="og:image" content={ogImage} />
<meta property="og:type" content="article" />
<meta property="og:url" content={canonicalURL} />
<meta property="og:description" content={description} />
<meta property="og:locale" content="en_GB" />

Now when you share a link to your blog post on social media, it will display the Open Graph image that you generated. You can use to test the Open Graph tags on your page.

The source code for this post is available in my blog’s GitHub repository.