OG Images for Newsletters: Make Your Archive Links Stand Out
How to add og:image and social preview metadata to newsletter archive pages — so every shared issue looks great on Twitter, LinkedIn, and Slack.
Why newsletters need OG images
Newsletter issues get shared publicly all the time — the author tweets a link, a reader posts it in Slack, a journalist references it on LinkedIn. If your archive URL shows a blank card, you're losing organic reach every time.
The challenge: most newsletter platforms (Beehiiv, Substack, ConvertKit, Ghost) have varying OG support. If you host your own archive, you have full control.
Platform-by-platform OG support
Substack
Substack auto-generates OG tags from your post title and subtitle. The OG image defaults to your publication cover if no post image is set. You cannot fully customize the OG image — the best option is to always include a post-level image, which Substack uses as og:image.
Beehiiv
Beehiiv includes OG title/description from post metadata. Set a thumbnail image on each issue to control og:image. You can also set custom meta descriptions per issue.
Ghost
Ghost has strong built-in OG support. Set a feature image + custom OG title/description per post via the post settings panel. The feature image becomes og:image.
ConvertKit (Kit)
ConvertKit landing pages have limited OG customization. For your archive, build a custom page on your own domain and pull issues via the ConvertKit API.
Self-hosted (Next.js, Astro, etc.)
Full control. See below for a dynamic OG image setup.
Dynamic OG images for self-hosted newsletter archives
If you self-host your newsletter archive, generate per-issue OG images dynamically:
// app/api/og/route.tsx
import { ImageResponse } from "next/og";
export const runtime = "edge";
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const title = searchParams.get("title") || "Newsletter Issue";
const issueNumber = searchParams.get("issue") || "";
const date = searchParams.get("date") || "";
return new ImageResponse(
(
<div
style={{
display: "flex",
flexDirection: "column",
background: "#faf5ff",
width: "100%",
height: "100%",
padding: "60px 80px",
justifyContent: "space-between",
}}
>
{/* Header */}
<div style={{ display: "flex", alignItems: "center", gap: 16 }}>
<div
style={{
fontSize: 28,
fontWeight: 700,
color: "#7c3aed",
letterSpacing: "-0.5px",
}}
>
📬 The Weekly Dev
</div>
{issueNumber && (
<div
style={{
fontSize: 18,
color: "#9ca3af",
marginLeft: "auto",
}}
>
Issue #{issueNumber}
</div>
)}
</div>
{/* Title */}
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
<div
style={{
fontSize: 52,
fontWeight: 900,
color: "#111827",
lineHeight: 1.2,
}}
>
{title}
</div>
{date && (
<div style={{ fontSize: 22, color: "#6b7280" }}>{date}</div>
)}
</div>
{/* Footer */}
<div
style={{
fontSize: 20,
color: "#9ca3af",
borderTop: "1px solid #e5e7eb",
paddingTop: 24,
}}
>
yournewsletter.com
</div>
</div>
),
{ width: 1200, height: 630 }
);
}Then in your issue page generateMetadata:
const ogImageUrl = new URL("/api/og", siteUrl);
ogImageUrl.searchParams.set("title", issue.title);
ogImageUrl.searchParams.set("issue", issue.number.toString());
ogImageUrl.searchParams.set("date", new Date(issue.publishedAt).toLocaleDateString());
return {
openGraph: {
images: [{ url: ogImageUrl.toString(), width: 1200, height: 630 }],
},
};What makes a great newsletter OG image
- Newsletter name prominent: Readers recognize your brand before reading the title
- Issue number: Builds a sense of archive depth ("Issue #142" signals longevity)
- Clean typography: No stock photos — clean type-only OGs perform best for newsletters
- Light background: Newsletter OG images tend to perform better with light backgrounds (vs. dark for dev tools)
- Date visible: Newsletters are time-sensitive; the date tells readers if this is worth clicking
Preview your newsletter OG tags
After setting up OG on your newsletter archive, paste any issue URL into OGFixer to verify how it looks on Twitter, LinkedIn, and Slack.
Check your OG tags free →