Tiny helpers to ship Open Graph images and page metadata in Next.js (Edge-ready). Drop-in defaults, easy overrides, and a dead-simple render API.
pnpm add @phucbm/next-og-imageRequires: Next.js 13.4+ (App Router), React 18+, Node 18+. Recommended:
export const runtime = "edge"for the OG route.
app/api/og-image/route.ts
import { renderOgImage } from "@phucbm/next-og-image";
export const runtime = "edge";
export async function GET(request: Request) {
return renderOgImage(request);
}app/(default)/page.tsx
import { generatePageMetadata } from "@phucbm/next-og-image";
export const generateMetadata = generatePageMetadata({
siteName: "Example Site",
title: "Example Site - Design. Code. Repeat.",
description: "A simple demo page for Open Graph metadata.",
canonicalPath: "/",
});Open: /api/og-image?siteName=Example&title=Hello&description=World
app/api/og-image/route.ts
import { renderOgImage } from "@phucbm/next-og-image";
import { OgImage } from "@/components/OgImage";
export const runtime = "edge";
export async function GET(request: Request) {
return renderOgImage(request, OgImage);
}Your
OgImagereceives{ siteName, title, description }as props.
import { generatePageMetadata } from "@phucbm/next-og-image";
export const generateMetadata = generatePageMetadata({
siteName: "Example Site",
title: "Example Site - Design. Code. Repeat.",
description: "A simple demo page for Open Graph metadata.",
canonicalPath: "/",
imageUrl: "/images/hero-image-01.jpg", // from /public/images/hero-image-01.jpg
});app/api/og-image/route.tsx
import { type OgImageRenderFn, renderOgImage } from "@phucbm/next-og-image";
import { OgImage } from "@/components/OgImage";
export const runtime = "edge";
export async function GET(request: Request) {
const origin = new URL(request.url).origin;
const logoUrl = new URL("/logo.png", origin).toString(); // /public/logo.png -> /logo.png
const render: OgImageRenderFn = (props) => <OgImage {...props} logoUrl={logoUrl} />;
return renderOgImage(request, render, { width: 1200, height: 630 });
}components/OgImage.tsx
import type { OgImageInput } from "@phucbm/next-og-image";
type Props = OgImageInput & { logoUrl?: string };
export function OgImage({ siteName, title, description, logoUrl }: Props) {
return (
<div style={{
width: "100%", height: "100%", display: "flex", flexDirection: "column",
justifyContent: "space-between", padding: "48px", color: "#141413",
background: "linear-gradient(135deg, #f0eee6, #faf9f6)"
}}>
<div style={{ display: "flex", alignItems: "center", gap: 16 }}>
{logoUrl && <img src={logoUrl} alt="" width="88" height="88" />}
{siteName && <div style={{ fontSize: 70, fontWeight: 800, letterSpacing: "-0.02em" }}>{siteName}</div>}
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "24px" }}>
<h1 style={{ fontSize: 120, fontWeight: 800, margin: 0 }}>{title}</h1>
{description && (
<p style={{ fontSize: 56, margin: 0, maxWidth: "1000px", opacity: 0.95 }}>
{description}
</p>
)}
</div>
</div>
);
}Important:
- Put the image in
/public/logo.pngand reference it as/logo.png. next/ogrequires an absolute URL inside the renderer; always build fromreq.url’s origin (as shown).
-
request: theRequestfrom your API route. -
render?:(props: OgImageInput) => ReactElement— custom renderer for the image.propsincludes{ siteName: string; title: string; description: string | null }.
-
options?:ImageResponseOptions(e.g.,{ width, height, headers, emoji, fonts, ... }).
Returns a new ImageResponse(...).
- Builds a Next.js
Metadataobject and wires the page’s OG/Twitter tags. - If
imageUrlis not provided, it generates a URL to your/api/og-imagewith the page’s values. - Accepts all your SEO fields plus any native
Metadatakeys.
Null-override behavior (for social image):
If you pass socialImage: { title: null }, that field is intentionally hidden (no fallback to page title).
undefined continues to fallback.
-
Route must handle JSX → name it
route.tsxif you inline JSX. -
Import React when using JSX in API routes:
import React from "react";
-
Absolute URLs only for
<img src>. Build fromnew URL(req.url).origin. -
SVGs are not supported by
next/og— use PNG/JPEG/WebP.
Projects using this package:
Use Social Share Preview (by Placid app) to quickly test OG images in the browser.
/api/og-image?siteName=Example&title=Hello&description=Ship+it
That’s it. Copy the snippets above into your app and you’re live.
MIT © PHUCBM