The Future of Web Dev
The Future of Web Dev
Customizable Open Graph Image Components for Next.js – ogimagecn
Add Open Graph image routes to Next.js apps with editable React components built for Satori and next/og.

ogimagecn is a shadcn registry of React components that generate Open Graph images for Next.js applications.
You can use it to build social sharing cards for blog posts, changelogs, product pages, portfolios, and documentation pages from local React component files.
Features
- 16 pre-built components for blogs, products, events, profiles, changelogs, quotes, and more.
- Built on Satori and rendered with
next/ogat request time. - Instant live previews that mirror the generated PNG output.
- Installs with the shadcn CLI into your project’s
components/ogdirectory. - Sensible default props with full local customization.
How To Use It
Table Of Contents
Installation
Make sure your Next.js project has shadcn/ui configured. Then add a component from the registry:
npx shadcn@latest add @ogimagecn/simpleThe installed file lands at:
components/og/simple.tsxFor repeated installs, add the registry namespace to components.json:
{
"registries": {
"@ogimagecn": "https://ogimagecn.dev/r/{name}.json"
}
}Then pull any component by name:
npx shadcn@latest add @ogimagecn/blogA full registry URL also works when you do not want to edit components.json:
npx shadcn@latest add https://ogimagecn.dev/r/blog.jsonThe registry path and alias workflow come from the shadcn registry format. ogimagecn maps each item to a JSON file under /r/ and copies the selected component into your local project.
Basic Usage
Create a route handler that returns an image response:
// app/og/route.tsx
import { ImageResponse } from "next/og";
import { Simple } from "@/components/og/simple";
export function GET(request: Request) {
const { searchParams } = new URL(request.url);
return new ImageResponse(
<Simple
title={searchParams.get("title") ?? "New article"}
description={
searchParams.get("description") ??
"Frontend notes, examples, and implementation tips."
}
brand="Your Site"
/>,
{
width: 1200,
height: 630,
}
);
}Visit this route in the browser during development:
/og?title=React%20Server%20Components&description=Practical%20notes%20for%20Next.js%20appsWire the Image Into Metadata
Point your page metadata to the generated route:
// app/page.tsx
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Frontend Notes",
description: "Practical implementation notes for modern web apps.",
openGraph: {
title: "Frontend Notes",
description: "Practical implementation notes for modern web apps.",
images: [
"/og?title=Frontend%20Notes&description=Modern%20web%20implementation%20notes",
],
},
twitter: {
card: "summary_large_image",
images: [
"/og?title=Frontend%20Notes&description=Modern%20web%20implementation%20notes",
],
},
};Use absolute URLs for metadata images when your deployment setup or social crawler requires them. In a production app, you may prefer a helper that builds the OG image URL from metadataBase, route params, or CMS content.
Create a Blog Post OG Route
A shared route works well for CMS-backed posts, MDX blogs, and static content collections:
// app/og/post/route.tsx
import { ImageResponse } from "next/og";
import { Blog } from "@/components/og/blog";
export function GET(request: Request) {
const { searchParams } = new URL(request.url);
const title = searchParams.get("title") ?? "Untitled post";
const author = searchParams.get("author") ?? "Editorial Team";
const category = searchParams.get("category") ?? "Frontend";
return new ImageResponse(
<Blog
title={title}
author={author}
category={category}
brand="Frontend Weekly"
/>,
{
width: 1200,
height: 630,
}
);
}Then map each post to its own preview URL:
// app/blog/[slug]/page.tsx
import type { Metadata } from "next";
type PageProps = {
params: Promise<{ slug: string }>;
};
const posts = {
"react-cache-patterns": {
title: "React Cache Patterns for App Router Projects",
description: "A practical guide to request memoization and cached data.",
author: "Jane Lee",
category: "React",
},
};
export async function generateMetadata({
params,
}: PageProps): Promise<Metadata> {
const { slug } = await params;
const post = posts[slug as keyof typeof posts];
if (!post) {
return {};
}
const image = `/og/post?title=${encodeURIComponent(
post.title
)}&author=${encodeURIComponent(post.author)}&category=${encodeURIComponent(
post.category
)}`;
return {
title: post.title,
description: post.description,
openGraph: {
title: post.title,
description: post.description,
images: [image],
},
twitter: {
card: "summary_large_image",
images: [image],
},
};
}Add a Custom Font
Load the font file and pass it to ImageResponse:
// app/og/route.tsx
import { ImageResponse } from "next/og";
import { Simple } from "@/components/og/simple";
export async function GET() {
const interSemiBold = await fetch(
new URL("./Inter-SemiBold.ttf", import.meta.url)
).then((response) => response.arrayBuffer());
return new ImageResponse(
<Simple
title="Design System Update"
description="New tokens, components, and layout rules."
brand="Acme UI"
/>,
{
width: 1200,
height: 630,
fonts: [
{
name: "Inter",
data: interSemiBold,
weight: 600,
style: "normal",
},
],
}
);
}Next.js accepts ttf, otf, and woff fonts in ImageResponse, with ttf or otf preferred for parsing speed.
All Available Components
| Category | Components | Use Cases |
|---|---|---|
| General content | Simple, Grid, Editorial | Article pages, landing pages, docs pages, and static site sections. |
| Publishing | Blog, Quote, Shiori | Blog posts, newsletter pages, quote cards, and curated reading pages. |
| Product and brand | Product, Logo, Showcase | Product launches, brand pages, directories, and feature announcements. |
| Updates and status | Changelog, Stat, Terminal | Release notes, metric snapshots, CLI tools, and developer updates. |
| Identity and events | Profile, Owner, Event, Photo | Personal sites, author pages, event announcements, and visual portfolios. |
Alternatives and Related Resources
- Modern Accessible shadcn/ui Blocks for Next.js & React
- 200+ Extended UI Components & Blocks for shadcn/ui
- Better Auth Components for Next.js and Shadcn UI
- Manage SEO Metadata in Svelte Applications
- Next.js ImageResponse API
FAQs
Q: Do I need a package install for ogimagecn?
A: No separate npm package is required for the selected component. Install each component through the shadcn CLI, then use the copied React file in your Next.js route.
Q: Why does my generated image fail after I edit the component?
A: The most common cause is unsupported CSS. Keep the OG component close to Satori-compatible JSX with inline flexbox styles.
Q: Does ogimagecn work with the Next.js App Router?
A: Yes. Use it in a route handler such as app/og/route.tsx or inside a file-based metadata route such as app/opengraph-image.tsx.
Q: Should the OG component use use client?
A: No. Keep the component render-only for image generation. Browser-specific logic does not belong inside JSX passed to ImageResponse.
Q: Can I create my own OG image component for the registry?
A: Yes. Add a new React file under the registry component folder, register it in registry.json, and build the registry output.
Q: The preview looks different from the actual OG image. What’s wrong?
A: Satori uses a strict CSS subset. If your customization introduced display: grid, absolute positioning, or unsupported properties, the runtime will ignore them. Stick to flexbox and inline styles, and cross-check with the live preview.
Q: How do I add a custom font?
A: Fetch the font file and pass its ArrayBuffer to the fonts option of ImageResponse. The first font becomes the default. See the practical example earlier in this article.
Q: Can I pre-generate OG images at build time for static export?
A: No. ImageResponse is a server runtime API; static export will not produce dynamic images. If you need static images, consider generating them with a build script and saving the PNGs to the public directory.





