OG Image API for Next.js, Remix & Astro
Every framework handles meta tags differently. Next.js has its Metadata API, Remix uses loader functions, and Astro leans on frontmatter and layouts. Here is how to integrate dynamic OG image generation into all three—with a single API, zero infrastructure, and under five minutes of setup per framework.
Why Framework-Native OG Images Matter
Social preview images drive clicks. A link shared on Twitter, LinkedIn, or Slack with a branded, readable card gets 2–3x more engagement than a bare URL or a generic fallback image. But generating those images at build time or with headless browsers adds complexity you do not need.
The better approach: point your og:image meta tag at an API endpoint that generates the image on demand. The social platform crawls your page, hits the URL, gets a PNG back. No build step. No Puppeteer. No image pipeline.
OGPeek gives you that endpoint. You pass parameters like title, subtitle, template, and brandColor via a URL, and the API returns a 1200×630 PNG in under 200ms. It works with any framework because it is just a URL—but each framework has its own idiomatic way to set meta tags. This guide covers the three most popular choices for modern web apps.
The OGPeek API in 30 Seconds
Before diving into framework code, here is the basic idea. You construct a URL with your parameters:
https://ogpeek.com/api/v1/og
?title=Your+Page+Title
&subtitle=A+short+description
&template=gradient
&theme=midnight
&brandColor=%23FF7A00
That URL returns a PNG image. You can paste it in a browser to preview it, or use it as the value of your og:image meta tag. The free tier gives you 50 images per day with a small watermark. Paid plans ($9/mo for 5,000 images, $29/mo for 50,000) remove the watermark and unlock the POST endpoint with API key authentication.
For a deeper walkthrough of parameters, templates, and themes, see the complete OG image API guide.
Next.js (App Router)
Next.js 13+ introduced the App Router with a new generateMetadata function that replaces the old <Head> component. This is the cleanest way to set OG images because metadata is colocated with your route.
Static Pages
For a page with a fixed title, export a metadata object directly:
// app/about/page.tsx
export const metadata = {
title: 'About Us',
openGraph: {
title: 'About Us',
description: 'Learn about our team and mission.',
images: [
{
url: 'https://ogpeek.com/api/v1/og'
+ '?title=About+Us'
+ '&subtitle=Our+Team+%26+Mission'
+ '&template=gradient'
+ '&theme=midnight'
+ '&brandColor=%230066FF',
width: 1200,
height: 630,
alt: 'About Us',
},
],
},
twitter: {
card: 'summary_large_image',
},
};
export default function AboutPage() {
return <main>{/* page content */}</main>;
}
Dynamic Pages (Blog Posts, Product Pages)
For routes with dynamic content, use generateMetadata to build the OG image URL from your data:
// app/blog/[slug]/page.tsx
import { getPost } from '@/lib/posts';
export async function generateMetadata({ params }) {
const post = await getPost(params.slug);
const ogUrl = new URL('https://ogpeek.com/api/v1/og');
ogUrl.searchParams.set('title', post.title);
ogUrl.searchParams.set('subtitle', post.category);
ogUrl.searchParams.set('template', 'gradient');
ogUrl.searchParams.set('theme', 'midnight');
ogUrl.searchParams.set('brandColor', '#FF7A00');
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
type: 'article',
images: [
{ url: ogUrl.toString(), width: 1200, height: 630 },
],
},
twitter: { card: 'summary_large_image' },
};
}
export default async function BlogPost({ params }) {
const post = await getPost(params.slug);
return (
<article>
<h1>{post.title}</h1>
<div>{post.content}</div>
</article>
);
}
Using URL and searchParams.set handles encoding automatically—no manual encodeURIComponent calls, no broken ampersands.
Next.js tip: If you are using the Pages Router instead of App Router, set the OG image in <Head> inside getStaticProps or getServerSideProps. The API URL is the same—only the meta tag delivery mechanism differs.
Remix
Remix handles meta tags through its meta export on route modules. Each route can define its own meta tags, including OG images, and Remix merges them with parent routes automatically.
Basic Route with Loader Data
// app/routes/blog.$slug.tsx
import type {
LoaderFunctionArgs,
MetaFunction,
} from '@remix-run/node';
import { json } from '@remix-run/node';
import { useLoaderData } from '@remix-run/react';
import { getPost } from '~/lib/posts.server';
export async function loader({ params }: LoaderFunctionArgs) {
const post = await getPost(params.slug!);
if (!post) throw new Response('Not Found', { status: 404 });
return json({ post });
}
export const meta: MetaFunction<typeof loader> = ({ data }) => {
if (!data) return [{ title: 'Not Found' }];
const { post } = data;
const ogImage = `https://ogpeek.com/api/v1/og?title=${
encodeURIComponent(post.title)
}&subtitle=${
encodeURIComponent(post.category)
}&template=gradient&theme=dark&brandColor=%23FF7A00`;
return [
{ title: post.title },
{ name: 'description', content: post.excerpt },
{ property: 'og:title', content: post.title },
{ property: 'og:description', content: post.excerpt },
{ property: 'og:type', content: 'article' },
{ property: 'og:image', content: ogImage },
{ property: 'og:image:width', content: '1200' },
{ property: 'og:image:height', content: '630' },
{ name: 'twitter:card', content: 'summary_large_image' },
{ name: 'twitter:image', content: ogImage },
];
};
export default function BlogPost() {
const { post } = useLoaderData<typeof loader>();
return (
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
</article>
);
}
Reusable Helper Function
If you have many routes that need OG images, extract a helper to keep your route files clean:
// app/lib/og.ts
export function buildOgImageUrl(
title: string,
subtitle?: string,
) {
const url = new URL('https://ogpeek.com/api/v1/og');
url.searchParams.set('title', title);
if (subtitle) url.searchParams.set('subtitle', subtitle);
url.searchParams.set('template', 'gradient');
url.searchParams.set('theme', 'midnight');
url.searchParams.set('brandColor', '#FF7A00');
return url.toString();
}
// Usage in any route:
// import { buildOgImageUrl } from '~/lib/og';
// const ogImage = buildOgImageUrl(post.title, post.category);
This pattern works well across any Remix project. You define your brand settings once and reuse them everywhere.
Astro
Astro is a content-focused framework that renders HTML at build time by default. Its component-based approach makes OG image integration especially clean because you can create a reusable <BaseHead> component that every page inherits.
Base Layout Component
---
// src/components/BaseHead.astro
interface Props {
title: string;
description: string;
ogSubtitle?: string;
}
const { title, description, ogSubtitle = '' } = Astro.props;
const ogUrl = new URL('https://ogpeek.com/api/v1/og');
ogUrl.searchParams.set('title', title);
if (ogSubtitle) ogUrl.searchParams.set('subtitle', ogSubtitle);
ogUrl.searchParams.set('template', 'gradient');
ogUrl.searchParams.set('theme', 'midnight');
ogUrl.searchParams.set('brandColor', '#FF7A00');
const ogImage = ogUrl.toString();
---
<meta charset="UTF-8" />
<meta name="viewport"
content="width=device-width, initial-scale=1.0" />
<title>{title}</title>
<meta name="description" content={description} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={ogImage} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content={ogImage} />
Using It in a Blog Post Layout
---
// src/layouts/BlogPost.astro
import BaseHead from '../components/BaseHead.astro';
const { frontmatter } = Astro.props;
---
<html lang="en">
<head>
<BaseHead
title={frontmatter.title}
description={frontmatter.description}
ogSubtitle={frontmatter.category}
/>
</head>
<body>
<article>
<h1>{frontmatter.title}</h1>
<slot />
</article>
</body>
</html>
Content Collections
If you are using Astro Content Collections (the recommended approach for blogs), the integration is seamless because each Markdown file already has typed frontmatter:
---
// 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>
Every blog post automatically gets a unique, branded OG image derived from its title and category—no manual image creation required.
Astro tip: Because Astro renders at build time, the OG image URL is baked into the static HTML. Social crawlers fetch the image directly from the OGPeek API at share time. This means your OG images are always live and can be updated by changing API parameters without rebuilding your site.
Advanced Patterns
Shared Configuration Across Frameworks
Across all three frameworks, the same pattern applies: centralize your OG image settings in one place. Define your template, theme, and brand color once, then reference them in every route or component. This keeps your social cards consistent and makes it trivial to rebrand later.
// og-config.ts — works in Next.js, Remix, or any Node project
export const OG_CONFIG = {
baseUrl: 'https://ogpeek.com/api/v1/og',
template: 'gradient',
theme: 'midnight',
brandColor: '#FF7A00',
} as const;
export function ogImageUrl(
title: string,
subtitle?: string,
): string {
const url = new URL(OG_CONFIG.baseUrl);
url.searchParams.set('title', title);
if (subtitle) url.searchParams.set('subtitle', subtitle);
url.searchParams.set('template', OG_CONFIG.template);
url.searchParams.set('theme', OG_CONFIG.theme);
url.searchParams.set('brandColor', OG_CONFIG.brandColor);
return url.toString();
}
Using the POST Endpoint for Production
The free GET endpoint is great for development and small sites. For production apps with high traffic, the POST endpoint with an API key gives you watermark-free images and higher rate limits. You can call it server-side to pre-warm images or use it in a build script:
// Server-side image generation (any framework)
const response = await fetch('https://ogpeek.com/api/v1/og', {
method: 'POST',
headers: {
'x-api-key': process.env.OGPEEK_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
title: 'My Blog Post',
subtitle: 'Published on March 28, 2026',
template: 'gradient',
theme: 'midnight',
brandColor: '#FF7A00',
}),
});
const imageBuffer = await response.arrayBuffer();
// Save to disk, upload to CDN, etc.
Pricing for Framework Developers
OGPeek is built for developers who want to ship fast and not think about image infrastructure. Here is how pricing maps to typical use cases:
- Free tier (50 images/day) — Perfect for personal blogs, side projects, and development. Small watermark included.
- Starter at $9/mo (5,000 images/month) — For production blogs, documentation sites, and small SaaS apps. No watermark, API key access.
- Pro at $29/mo (50,000 images/month) — For high-traffic SaaS, marketplaces, and platforms where users generate shareable content.
Most developer blogs fit comfortably within the free tier. You only need a paid plan when you are generating thousands of unique images per month or need watermark-free output.
Common Pitfalls
A few things to watch out for when integrating OG images into any framework:
- URL encoding: Always encode your title and subtitle. Characters like
&,#, and+will break your URL if not encoded. UseencodeURIComponentor theURLAPI as shown in the examples above. - Caching: Social platforms cache OG images aggressively. If you change your image parameters, use the Twitter Card Validator or Facebook Sharing Debugger to force a re-scrape. See our Twitter card image guide for details.
- Image dimensions: Always include
og:image:widthandog:image:heightmeta tags set to1200and630. Some platforms will not display the image without explicit dimensions. - HTTPS required: All
og:imageURLs must use HTTPS. OGPeek serves everything over HTTPS by default. - Title length: Keep titles under 60 characters for best results. Longer titles will be truncated or reduced in font size to fit the 1200×630 canvas.
Framework Comparison at a Glance
Here is a quick summary of where you set OG meta tags in each framework:
- Next.js App Router: Export
metadataorgenerateMetadatafrom yourpage.tsxfile. - Next.js Pages Router: Use
<Head>fromnext/headinside your component, with data fromgetStaticPropsorgetServerSideProps. - Remix: Export a
metafunction from your route module. Access loader data via thedataargument. - Astro: Use component props in the frontmatter fence (
---) and render meta tags in your layout’s<head>.
In every case, the OGPeek API URL is identical. The only thing that changes is how your framework delivers that URL to the <meta> tag in the HTML output.
Start generating OG images now
50 free images per day. No signup required. Works with Next.js, Remix, Astro, and every other framework.
Open the playground →Wrapping Up
Dynamic OG image generation should not require a different approach for every framework. The pattern is the same everywhere: construct a URL with your title, template, and brand settings, then set it as the og:image meta tag using whatever mechanism your framework provides.
Next.js gives you generateMetadata. Remix gives you the meta export. Astro gives you component props and frontmatter. All three work with a single OGPeek API URL.
If you are building a SaaS or a content-heavy site in any of these frameworks, automating your social preview images with an API is one of the highest-leverage changes you can make for click-through rates. Set it up once, and every new page automatically gets a branded, professional social card.
For more on OG images, check out our complete API guide, our image size reference, the static site generator integration guide, or our guide to automating OG images for SaaS blogs. And explore the full Peek Suite for more developer APIs including tech stack detection, SEO audits, and cron monitoring.