March 29, 2026 · 8 min read

How to Add Dynamic OG Images to Astro Sites with OGPeek API

Astro is built for speed—static HTML, zero JavaScript by default, island architecture. But generating unique Open Graph images for every page has always been a pain point. Here's how to solve it in under five minutes with a single API URL, no build plugins, and no headless browsers.

Dynamic OG image for Astro sites generated by OGPeek API

The OG Image Problem in Astro

Astro excels at producing fast, static websites. Whether you use the default static output mode or opt into SSR with an adapter, the framework prioritizes shipping minimal HTML and CSS to the browser. That philosophy is great for performance, but it creates a gap when it comes to dynamic image generation.

When someone shares your Astro blog post on Twitter/X, LinkedIn, or Slack, the platform's crawler fetches your page, reads the og:image meta tag, and requests that image. If you are using a single static image for every page—or worse, no OG image at all—your links look generic and get fewer clicks.

The traditional solutions each come with trade-offs:

There is a simpler approach: point your og:image meta tag at an API that returns a PNG. Your Astro site stays static. The image generation happens externally, on demand, when a social platform actually requests it.

How OGPeek Works

OGPeek is an OG image generation API. You construct a URL with your title, subtitle, template, and brand color as query parameters. When that URL is requested, OGPeek renders the image and returns a 1200x630 PNG—the standard size for Open Graph images across all major platforms.

GET https://ogpeek.com/api/v1/og
  ?title=Your+Page+Title
  &subtitle=yourdomain.com
  &template=gradient
  &theme=midnight
  &brandColor=%23FF7A00

That is the entire integration. A GET request returns a PNG. No API keys for the free tier, no SDK to install, no build configuration to change. You embed this URL in your Astro layout's <head> and every page gets a unique social card based on its title.

Key point: Social platform crawlers follow the URL in your og:image meta tag and fetch whatever is there. They cannot distinguish between a static PNG on your CDN and an API endpoint that generates a PNG on the fly. Both are just HTTP responses with Content-Type: image/png.

Step 1: Add OGPeek to Your Base Layout

Most Astro projects use a shared layout component for the <html> wrapper, <head> tags, and common page structure. This is where the OG image integration goes.

Open your base layout (typically src/layouts/BaseLayout.astro) and add the OGPeek URL in the frontmatter, then reference it in the <head>:

---
// src/layouts/BaseLayout.astro
interface Props {
  title: string;
  description?: string;
}

const { title, description } = Astro.props;
const siteUrl = 'https://yourdomain.com';

const ogImageUrl = `https://ogpeek.com/api/v1/og?title=${
  encodeURIComponent(title)
}&subtitle=${
  encodeURIComponent('yourdomain.com')
}&template=gradient&theme=midnight&brandColor=%23FF7A00`;
---

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width" />
  <title>{title}</title>
  <meta name="description" content={description} />

  <!-- Open Graph -->
  <meta property="og:title" content={title} />
  <meta property="og:description" content={description} />
  <meta property="og:image" content={ogImageUrl} />
  <meta property="og:image:width" content="1200" />
  <meta property="og:image:height" content="630" />

  <!-- Twitter Card -->
  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:title" content={title} />
  <meta name="twitter:description" content={description} />
  <meta name="twitter:image" content={ogImageUrl} />
</head>
<body>
  <slot />
</body>
</html>

That is the full integration. Every page that uses BaseLayout now gets a unique OG image derived from its title prop. No additional packages, no build step changes, no environment variables.

Step 2: Use It with Astro Content Collections

If you are using Astro's content collections for blog posts or documentation, the title comes from your Markdown or MDX frontmatter. Here is how a typical blog post layout wires it up:

---
// src/layouts/BlogPost.astro
import BaseLayout from './BaseLayout.astro';

interface Props {
  frontmatter: {
    title: string;
    description: string;
    pubDate: string;
  };
}

const { frontmatter } = Astro.props;
---

<BaseLayout title={frontmatter.title} description={frontmatter.description}>
  <article>
    <h1>{frontmatter.title}</h1>
    <time>{frontmatter.pubDate}</time>
    <slot />
  </article>
</BaseLayout>

Now every Markdown file in your src/content/blog/ directory automatically gets a branded OG image based on its frontmatter title. A post titled "Getting Started with Astro" produces a social card with that exact text—no manual image creation needed.

Dynamic Collection Pages

For programmatic routes using getStaticPaths(), the same pattern applies. Pass the title from your collection entry into the layout:

---
// src/pages/blog/[slug].astro
import { getCollection } from 'astro:content';
import BlogPost from '../../layouts/BlogPost.astro';

export async function getStaticPaths() {
  const posts = await getCollection('blog');
  return posts.map((post) => ({
    params: { slug: post.slug },
    props: { post },
  }));
}

const { post } = Astro.props;
const { Content } = await post.render();
---

<BlogPost frontmatter={post.data}>
  <Content />
</BlogPost>

Each slug resolves to a static HTML page at build time, and each page's og:image meta tag points to a unique OGPeek URL with the post's title encoded into it.

Customizing Your OG Images

OGPeek supports several parameters to match your brand:

You can also make the subtitle dynamic. For example, pass the blog category or author name:

const ogImageUrl = `https://ogpeek.com/api/v1/og?title=${
  encodeURIComponent(title)
}&subtitle=${
  encodeURIComponent(`${category} · yourdomain.com`)
}&template=gradient&theme=midnight&brandColor=%23FF7A00`;

OGPeek vs @vercel/og vs Manual Creation

Here is how the three main approaches compare for Astro projects:

Feature OGPeek API @vercel/og Manual (Figma)
Setup time 2 minutes 15–30 minutes 5–10 min per image
Requires serverless function No Yes (Edge API route) No
Hosting lock-in None Vercel only None
Works with static output Yes No (needs SSR adapter) Yes
Build time impact Zero Zero (runtime) Zero
Scales to 1000+ pages Yes Yes No
Custom CSS layout Pre-built templates JSX with CSS subset Full control
Dependencies added 0 @vercel/og + Satori 0

For most Astro sites—blogs, documentation, marketing pages—OGPeek's pre-built templates cover the common use cases without requiring you to write and maintain image rendering code. If you need pixel-perfect custom layouts with arbitrary HTML/CSS, @vercel/og gives you more control at the cost of hosting lock-in and additional complexity.

Tip: If you deploy your Astro site to Netlify, Cloudflare Pages, or AWS Amplify, @vercel/og is not an option without workarounds. OGPeek works with any hosting provider because the image generation happens on OGPeek's servers, not yours.

Testing Your OG Images

After deploying your Astro site, verify your OG images work correctly:

  1. View the source of any page and confirm the og:image meta tag contains a valid OGPeek URL with your page title.
  2. Open the URL directly in your browser. You should see a 1200x630 PNG with your title rendered on it.
  3. Use a debugger tool like the Twitter Card Validator or Facebook Sharing Debugger to see exactly what the platform will render.
  4. Share a link in a private Slack channel or Discord server to see the preview in context.

If the image does not appear, check that your encodeURIComponent() call is encoding special characters in your title correctly. Ampersands, quotes, and hash symbols in titles can break the URL if left unencoded.

Performance Considerations

OGPeek caches generated images at the CDN edge. The first request for a given URL renders the image and caches it. Subsequent requests—including from different social platforms sharing the same link—are served from the cache with sub-50ms response times.

Since the og:image URL is only fetched by social platform crawlers (not by your site's visitors), there is zero impact on your Astro site's performance metrics. Core Web Vitals, Lighthouse scores, and page load times are completely unaffected.

Start generating OG images for your Astro site

Read the full API documentation, explore templates, and grab your integration URL. Free tier available—no signup required.

Read the API docs →

Conclusion

Astro's static-first architecture makes it one of the fastest frameworks for building content sites. Adding dynamic OG images should not compromise that speed or simplicity. With OGPeek, you add a URL to your base layout, pass the page title as a parameter, and every page on your site gets a unique, branded social preview card.

No build plugins. No headless browsers. No serverless functions. No hosting lock-in. Just a GET request that returns a PNG.

Your Astro site stays static. Your OG images stay dynamic.

More developer APIs from the Peek Suite