An Open Graph image or OG image is the image that is displayed on social media accounts when you or someone else post a link to an article or a video from your website. By default, Open Graph meta tags determine how URLs are displayed when shared on social media sites.
Setting Up the API Route
The cornerstone of our dynamic OG image generation is the og/route.js
file:
import { ImageResponse } from "next/og";
export function GET(request: Request) {
let url = new URL(request.url);
let title = url.searchParams.get("title") || "Next.js Blog";
try {
return new ImageResponse(
// JSX structure here
);
} catch (error) {
return new Response("Failed to create OG Image", { status: 500 });
}
}
This API route uses Next.js's ImageResponse to generate our OG image on-the-fly. We extract the title from the URL parameters, defaulting to "Next.js Blog" if no title is provided.
Designing the OG Image
The heart of our OG image generation lies in the JSX structure we pass to ImageResponse:
<div
style={{
display: "flex",
height: "100%",
width: "100%",
alignItems: "center",
justifyContent: "center",
letterSpacing: "-.02em",
fontWeight: 700,
background: "white",
}}
>
{/* Logo and website URL */}
<div
style={{
left: 42,
top: 42,
position: "absolute",
display: "flex",
alignItems: "center",
}}
>
<span
style={{
width: 24,
height: 24,
background: "black",
color: "white",
padding: "2px 25px 2px 2px",
}}
>
BLOG
</span>
<span
style={{
marginLeft: 8,
fontSize: 20,
}}
>
// your website URL
</span>
</div>
{/* Blog post title */}
<div
style={{
display: "flex",
flexWrap: "wrap",
justifyContent: "center",
padding: "20px 50px",
margin: "0 42px",
fontSize: 40,
width: "auto",
maxWidth: 550,
textAlign: "center",
backgroundColor: "black",
color: "white",
lineHeight: 1.4,
}}
>
{title}
</div>
</div>
This structure creates a visually appealing OG image with:
- A white background
- A logo and website URL in the top-left corner
- The blog post title centered in a black box
Integrating with Blog Posts
To integrate this dynamic OG image generation into our blog post pages, we use the generateMetadata
function in blog/[slug]/page.jsx
:
export function generateMetadata({
params,
}: {
params: { slug: string };
}) {
let post = getBlogPosts().find((post) => post.slug === params.slug);
if (!post) {
return;
}
let {
title,
publishedAt: publishedTime,
summary: description,
image,
} = post.metadata;
let ogImage = image
? image
: `${baseUrl}/og?title=${encodeURIComponent(title)}`;
return {
title,
description,
openGraph: {
title,
description,
type: "article",
publishedTime,
url: `${baseUrl}/blog/${post?.slug}}`,
images: [{ url: ogImage }],
},
twitter: {
card: "summary_large_image",
title,
description,
images: [ogImage],
},
};
}
This function creates metadata for each blog post, including OpenGraph and Twitter card metadata for optimal sharing.
SEO Optimization
To enhance SEO, we include structured data using JSON-LD:
<script
type="application/ld+json"
suppressHydrationWarning
dangerouslySetInnerHTML={{
__html: JSON.stringify({
"@context": "https://schema.org",
"@type": "BlogPosting",
headline: post?.metadata.title,
datePublished: post?.metadata?.publishedAt,
dateModified: post.metadata.publishedAt,
description: post.metadata.summary,
image: post.metadata.image
? `${baseUrl}${post.metadata.image}`
: `/og?title=${encodeURIComponent(post.metadata.title)}`,
url: `${baseUrl}/blog/${post.slug}`,
author: {
"@type": "Person",
name: "Next.js Blog",
},
}),
}}
/>
This structured data helps search engines better understand and index our content.
Performance Considerations
To optimize performance, we use generateStaticParams
:
export async function generateStaticParams() {
let posts = getBlogPosts();
return posts.map((post) => ({
slug: post.slug,
}));
}
This function pre-renders our blog post pages at build time, improving load times and SEO.