I recently added an /api/og to create OG imges
import { ImageResponse } from "next/og";import { mapSanityEvents } from "@/lib/map-sanity-event";import { getOgMetaData, normalizeOgText, OG_DEFAULT_HOME_TITLE,} from "./og-config";import { getBlogPageOGData, getGenericPageOGData, getHomePageOGData, getSlugPageOGData, getUpcomingEventsForOg,} from "./og-data";import { OgEventsImage } from "./og-events-template";import { getOgImageOptions } from "./og-fonts";import { OgPlatformImage } from "./og-platform-template";import { OgBrandedImage, OgErrorImage, OgSeoImage, type OgTemplateData,} from "./og-template";
type ContentProps = Record<string, string>;
type OgFetcherResult = OgTemplateData & { seoImage?: string | null };
type OgPlatformItemResult = { title?: string | null; tagline?: string | null;};
type PageOgResult = OgFetcherResult & { hasEventsListing?: boolean; hasPlatformList?: boolean; platformItems?: OgPlatformItemResult[] | null;};
function mapPlatformItemsForOg( items: OgPlatformItemResult[] | null | undefined,) { return (items ?? []) .map((item) => ({ title: normalizeOgText(item?.title), tagline: normalizeOgText(item?.tagline) || null, })) .filter((item) => item.title.length > 0);}
function renderPlatformOg( page: PageOgResult, options?: { defaultTitle?: string; fallbackTitle?: string },) { if (!page.hasPlatformList) return null;
const items = mapPlatformItemsForOg(page.platformItems); if (items.length === 0) return null;
const title = normalizeOgText(page.title) || options?.defaultTitle || options?.fallbackTitle || "Platform";
return ( <OgPlatformImage title={title} logo={normalizeOgText(page.logo) || undefined} items={items} /> );}
async function resolveOgContent( { id }: ContentProps, fetcher: ( documentId: string, ) => Promise<[OgFetcherResult | null | undefined, string | undefined]>, options?: { defaultTitle?: string },) { if (!id) return null;
const [result, err] = await fetcher(id); if (err || !result) return null;
const seoImage = normalizeOgText(result.seoImage); if (seoImage) { return <OgSeoImage seoImage={seoImage} />; }
const title = normalizeOgText(result.title) || options?.defaultTitle || undefined;
return ( <OgBrandedImage description={result.description} image={result.image} logo={result.logo} title={title} /> );}
async function resolveSlugPageOgContent({ id }: ContentProps) { if (!id) return null;
const [result, err] = await getSlugPageOGData(id); if (err || !result) return null;
const page = result as PageOgResult;
const seoImage = normalizeOgText(page.seoImage); if (seoImage) { return <OgSeoImage seoImage={seoImage} />; }
if (page.hasEventsListing) { const [events, eventsErr] = await getUpcomingEventsForOg(); if (!eventsErr && events && events.length > 0) { const upcoming = mapSanityEvents(events); return ( <OgEventsImage title={normalizeOgText(page.title) || "Events"} logo={normalizeOgText(page.logo) || undefined} events={upcoming} /> ); } }
const platformOg = renderPlatformOg(page, { fallbackTitle: "Platform" }); if (platformOg) return platformOg;
return ( <OgBrandedImage description={page.description} image={page.image} logo={page.logo} title={page.title} /> );}
async function resolveHomePageOgContent({ id }: ContentProps) { if (!id) return null;
const [result, err] = await getHomePageOGData(id); if (err || !result) return null;
const page = result as PageOgResult;
const seoImage = normalizeOgText(page.seoImage); if (seoImage) { return <OgSeoImage seoImage={seoImage} />; }
const platformOg = renderPlatformOg(page, { defaultTitle: OG_DEFAULT_HOME_TITLE, }); if (platformOg) return platformOg;
const title = normalizeOgText(page.title) || OG_DEFAULT_HOME_TITLE || undefined;
return ( <OgBrandedImage description={page.description} image={page.image} logo={page.logo} title={title} /> );}
const getHomePageContent = (props: ContentProps) => resolveHomePageOgContent(props);
const getSlugPageContent = (props: ContentProps) => resolveSlugPageOgContent(props);
const getBlogPageContent = (props: ContentProps) => resolveOgContent(props, getBlogPageOGData);
const getGenericPageContent = (props: ContentProps) => resolveOgContent(props, getGenericPageOGData);
const block = { homePage: getHomePageContent, page: getSlugPageContent, blog: getBlogPageContent,} as const;
export async function GET({ url }: Request): Promise<ImageResponse> { const { searchParams } = new URL(url); const type = searchParams.get("type") as keyof typeof block; const { width, height } = getOgMetaData(searchParams); const para = Object.fromEntries(searchParams.entries()); const options = await getOgImageOptions({ width, height }); const loadContent = block[type] ?? getGenericPageContent;
try { const content = await loadContent(para); return new ImageResponse(content ?? <OgErrorImage />, options); } catch (error) { console.error("OG image generation failed:", error); return new ImageResponse(<OgErrorImage />, options); }}However, I was looking at my usage and dear lord 2 MB of ingress and egress for some images?!?!
