Skip to content

Instantly share code, notes, and snippets.

@igeligel
Created July 9, 2026 06:10
Show Gist options
  • Select an option

  • Save igeligel/b6738f0c94fd32f95deef0aac51728fd to your computer and use it in GitHub Desktop.

Select an option

Save igeligel/b6738f0c94fd32f95deef0aac51728fd to your computer and use it in GitHub Desktop.
import { ImageResponse } from "@vercel/og";
import type { NextApiRequest, NextApiResponse } from "next";
function dataUriToArrayBuffer(uri: string): ArrayBuffer {
const match = uri.match(
/^data:image\/(png|jpeg|webp|gif|svg\+xml);base64,(.+)$/,
);
if (!match)
throw new Error("Unsupported image format: expected data:image/*;base64,...");
const buf = Buffer.from(match[2], "base64");
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
}
let fontCache: Record<string, ArrayBuffer> | null = null;
async function getInterFonts() {
if (fontCache) {
const c = fontCache;
return [
{
name: "Inter",
data: c["400"],
weight: 400 as const,
style: "normal" as const,
},
{
name: "Inter",
data: c["600"],
weight: 600 as const,
style: "normal" as const,
},
{
name: "Inter",
data: c["700"],
weight: 700 as const,
style: "normal" as const,
},
];
}
async function load(weight: number) {
const cssUrl = `https://fonts.googleapis.com/css2?family=Inter:wght@${weight}&display=swap`;
const css = await fetch(cssUrl).then((r) => r.text());
const match = css.match(/url\(([^)]+)\)/);
if (!match)
throw new Error(`Could not resolve font URL for Inter ${weight}`);
const fontUrl = match[1].replace(/['"]/g, "");
return fetch(fontUrl).then((r) => r.arrayBuffer());
}
const [w400, w600, w700] = await Promise.all([
load(400),
load(600),
load(700),
]);
fontCache = { "400": w400, "600": w600, "700": w700 };
return [
{
name: "Inter",
data: w400,
weight: 400 as const,
style: "normal" as const,
},
{
name: "Inter",
data: w600,
weight: 600 as const,
style: "normal" as const,
},
{
name: "Inter",
data: w700,
weight: 700 as const,
style: "normal" as const,
},
];
}
const TEAL = "#11998E";
const MINT = "#38EF7D";
const GREEN = "#0EB34D";
const INK = "#2C1702";
const SCALE = 2;
const OG_WIDTH = 1200 * SCALE;
const OG_HEIGHT = 630 * SCALE;
const DEFAULT_POINTER_LEFT = 1002 * SCALE;
const DEFAULT_POINTER_TOP = 408 * SCALE;
const DEFAULT_POINTER_WIDTH = 52 * SCALE;
const DEFAULT_POINTER_HEIGHT = 62 * SCALE;
type Body = {
cardImageSrc: string;
companyName: string;
logoSrc: string;
heading: string;
description: string;
cardPointerLeftPx: number;
cardPointerTopPx: number;
};
function LogoMark() {
const bars = [
{ w: 9, h: 26, rotate: -10 },
{ w: 11, h: 44, rotate: -4 },
{ w: 10, h: 34, rotate: 6 },
{ w: 9, h: 22, rotate: 14 },
{ w: 8, h: 18, rotate: 22 },
];
return (
<div
style={{
display: "flex",
alignItems: "flex-end",
height: "72px",
gap: "4px",
}}
>
{bars.map((b, i) => (
<div
key={i}
style={{
width: `${b.w}px`,
height: `${b.h}px`,
borderRadius: "9999px",
backgroundImage: `linear-gradient(to bottom, ${TEAL}, ${MINT})`,
transform: `rotate(${b.rotate}deg)`,
transformOrigin: "bottom center",
}}
/>
))}
</div>
);
}
function validateBody(raw: Record<string, unknown>): Body {
const cardImageSrc =
typeof raw.cardImageSrc === "string" && raw.cardImageSrc.trim()
? raw.cardImageSrc.trim()
: "";
const heading =
typeof raw.heading === "string" && raw.heading.trim()
? raw.heading.trim()
: "";
const companyName =
typeof raw.companyName === "string" ? raw.companyName : "";
const logoSrc =
typeof raw.logoSrc === "string"
? raw.logoSrc
: typeof raw.logoUrl === "string"
? raw.logoUrl
: "";
const description =
typeof raw.description === "string" ? raw.description : "";
const cardPointerLeftPx =
typeof raw.cardPointerLeftPx === "number" &&
Number.isFinite(raw.cardPointerLeftPx)
? Math.max(0, Math.min(OG_WIDTH, raw.cardPointerLeftPx * SCALE))
: DEFAULT_POINTER_LEFT;
const cardPointerTopPx =
typeof raw.cardPointerTopPx === "number" &&
Number.isFinite(raw.cardPointerTopPx)
? Math.max(0, Math.min(OG_HEIGHT, raw.cardPointerTopPx * SCALE))
: DEFAULT_POINTER_TOP;
return {
cardImageSrc,
heading,
companyName,
logoSrc,
description,
cardPointerLeftPx,
cardPointerTopPx,
};
}
function truncated(s: string, max: number): string {
return s.length <= max ? s : s.slice(0, max) + "…";
}
export const maxDuration = 300;
export const config = {
api: {
bodyParser: {
sizeLimit: "4mb",
},
},
};
/**
* Renders a blogengine OG image server-side using @vercel/og (satori + resvg).
*
* POST /api/og-render-image
* Body: { cardImageSrc, heading, companyName?, logoSrc?, description?,
* cardPointerLeftPx?, cardPointerTopPx? }
* Accepts logoSrc (or logoUrl for backwards compatibility).
*
* Returns: { image: "data:image/png;base64,...", width: 1200, height: 630 }
*/
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
if (req.method !== "POST") {
res.setHeader("Allow", "POST");
return res.status(405).json({ error: "Method not allowed" });
}
const body = validateBody(req.body);
if (!body.heading) {
return res.status(400).json({ error: "heading is required" });
}
if (!body.cardImageSrc) {
return res.status(400).json({ error: "cardImageSrc is required" });
}
const tealPanelLeft = 433 * SCALE;
const tealPanelLeftTop = -206.813 * SCALE;
const tealPanelLeftWidth = 1113.87 * SCALE;
const tealPanelLeftBottom = 1335.46 * SCALE;
const brandLeft = 60 * SCALE;
const brandTop = 160 * SCALE;
const brandWidth = 540 * SCALE;
const brandHeight = 72 * SCALE;
const brandFontSize = 34 * SCALE;
const brandGap = 14 * SCALE;
const tealPanelRight = 570 * SCALE;
const tealPanelRightTop = -200 * SCALE;
const tealPanelRightWidth = 1000 * SCALE;
const tealPanelRightBottom = 1500 * SCALE;
const cardScreenshotLeft = 600 * SCALE;
const cardScreenshotTop = 140 * SCALE;
const cardScreenshotWidth = 750 * SCALE;
const cardScreenshotHeight = 550 * SCALE;
const cardScreenshotBorderRadius = 16 * SCALE;
const headingWidth = 420 * SCALE;
const headingFontSize = 52 * SCALE;
const descriptionWidth = 450 * SCALE;
const descriptionFontSize = 24 * SCALE;
//
const pointerFilterShadow = 4 * SCALE;
const pointerFilter = `drop-shadow(0px ${pointerFilterShadow}px ${pointerFilterShadow}px rgba(0,0,0,0.22))`;
try {
const fonts = await getInterFonts();
const cardImageBuf = body.cardImageSrc.startsWith("data:")
? dataUriToArrayBuffer(body.cardImageSrc)
: null;
const logoBuf = body.logoSrc.startsWith("data:")
? dataUriToArrayBuffer(body.logoSrc)
: null;
const response = new ImageResponse(
<div
style={{
width: OG_WIDTH,
height: OG_HEIGHT,
display: "flex",
position: "relative",
backgroundColor: "white",
overflow: "hidden",
}}
>
{/* Rotated teal panel — left background */}
<div
style={{
position: "absolute",
left: `${tealPanelLeft}px`,
top: `${tealPanelLeftTop}px`,
width: `${tealPanelLeftWidth}px`,
height: `${tealPanelLeftBottom}px`,
backgroundImage: `linear-gradient(135deg, ${TEAL}, ${MINT})`,
transform: "rotate(-11.9264deg)",
transformOrigin: "top left",
zIndex: 0,
}}
/>
<div
style={{
position: "absolute",
left: `${brandLeft}px`,
top: `${brandTop}px`,
display: "flex",
flexDirection: "column",
}}
>
<div
style={{
display: "flex",
gap: `${brandGap}px`,
alignItems: "center",
}}
>
<div
style={{
maxWidth: `${brandWidth}px`,
display: "flex",
}}
>
{body.logoSrc.trim() ? (
<img
src={(logoBuf ?? body.logoSrc) as unknown as string}
style={{
maxWidth: `${brandWidth}px`,
height: `${brandHeight}px`,
objectFit: "contain",
objectPosition: "left center",
}}
/>
) : (
<LogoMark />
)}
</div>
{body.companyName.trim() ? (
<div
style={{
fontSize: `${brandFontSize}px`,
fontWeight: 700,
color: INK,
fontFamily: "Inter",
WebkitFontSmoothing: "antialiased",
MozOsxFontSmoothing: "grayscale",
textRendering: "optimizeLegibility",
}}
>
{truncated(body.companyName, 40)}
</div>
) : null}
</div>
<div
style={{
maxWidth: `${headingWidth}px`,
// color: TEAL,
color: "transparent",
backgroundImage: `linear-gradient(135deg, ${TEAL}, ${GREEN})`,
backgroundClip: "text",
WebkitBackgroundClip: "text",
WebkitTextFillColor: "transparent",
fontSize: `${headingFontSize}px`,
fontWeight: 700,
fontFamily: "Inter",
WebkitFontSmoothing: "antialiased",
MozOsxFontSmoothing: "grayscale",
textRendering: "optimizeLegibility",
lineHeight: 1.1,
display: "flex",
flexDirection: "column",
zIndex: 2,
marginTop: "1rem",
paddingBottom: "0.2rem",
}}
>
{truncated(body.heading, 120)}
</div>
{body.description.trim() ? (
<div
style={{
marginTop: "1rem",
maxWidth: `${descriptionWidth}px`,
color: "transparent",
backgroundImage: `linear-gradient(135deg, ${TEAL}, ${GREEN})`,
backgroundClip: "text",
WebkitBackgroundClip: "text",
WebkitTextFillColor: "transparent",
fontSize: `${descriptionFontSize}px`,
fontWeight: 600,
fontFamily: "Inter",
WebkitFontSmoothing: "antialiased",
MozOsxFontSmoothing: "grayscale",
textRendering: "optimizeLegibility",
lineHeight: 1.38,
display: "flex",
flexDirection: "column",
zIndex: 2,
}}
>
{truncated(body.description, 300)}
</div>
) : null}
</div>
{/* Heading */}
{/* Description */}
{/* Rotated teal panel — right background */}
<div
style={{
position: "absolute",
left: `${tealPanelRight}px`,
top: `${tealPanelRightTop}px`,
width: `${tealPanelRightWidth}px`,
height: `${tealPanelRightBottom}px`,
backgroundImage: `linear-gradient(180deg, ${TEAL}, ${MINT})`,
transform: "rotate(-12deg)",
zIndex: 3,
}}
/>
{/* Card screenshot — right side, after teal panel */}
<div
style={{
position: "absolute",
left: `${cardScreenshotLeft}px`,
top: `${cardScreenshotTop}px`,
width: `${cardScreenshotWidth}px`,
height: `${cardScreenshotHeight}px`,
borderRadius: "16px",
overflow: "visible",
transform: "rotate(-5deg)",
transformOrigin: "top left",
zIndex: 4,
boxShadow: "0 4px 20px 0 rgba(0,0,0,0.25)",
display: "flex",
}}
>
<img
src={(cardImageBuf ?? body.cardImageSrc) as unknown as string}
style={{
overflow: "hidden",
height: "100%",
borderRadius: `${cardScreenshotBorderRadius}px`,
objectFit: "cover",
objectPosition: "left top",
}}
/>
</div>
{/* Pointer accent SVG */}
<div
style={{
position: "absolute",
display: "flex",
left: `${body.cardPointerLeftPx}px`,
top: `${body.cardPointerTopPx}px`,
zIndex: 5,
filter: `${pointerFilter}`,
}}
>
<svg
width={`${DEFAULT_POINTER_WIDTH}px`}
height={`${DEFAULT_POINTER_HEIGHT}px`}
viewBox="1006 408 52 62"
fill="none"
xmlns="http://www.w3.org/2000/svg"
style={{ display: "flex" }}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M1013.36 460.993L1006 413.515L1045.7 442.633L1025.6 445.75L1024.44 446.307L1013.36 460.993Z"
fill="white"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M1040.6 458.817L1030.62 465.024L1011.64 434.302L1021.86 428.004L1040.6 458.817Z"
fill="white"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M1036.34 457.412L1031.23 460.554L1018.65 440.118L1023.75 436.974L1036.34 457.412Z"
fill="black"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M1010.07 420.192L1015.21 453.359L1022.7 443.498L1023.9 442.89L1038.04 440.698L1010.07 420.192Z"
fill="black"
/>
</svg>
</div>
</div>,
{
width: OG_WIDTH,
height: OG_HEIGHT,
fonts,
},
);
const buffer = Buffer.from(await response.arrayBuffer());
const base64 = buffer.toString("base64");
return res.status(200).json({
image: `data:image/png;base64,${base64}`,
width: OG_WIDTH,
height: OG_HEIGHT,
});
} catch (err) {
const msg = err instanceof Error ? err.message : "Unknown error";
return res.status(500).json({ error: msg });
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment