Skip to content

Opengrph fixes#8

Merged
JacobCoffee merged 4 commits intomainfrom
opengrph-fixes
Feb 28, 2026
Merged

Opengrph fixes#8
JacobCoffee merged 4 commits intomainfrom
opengrph-fixes

Conversation

@JacobCoffee
Copy link
Member

before
image
image

after
image
image

Copilot AI review requested due to automatic review settings February 28, 2026 01:18
@JacobCoffee JacobCoffee merged commit b0b11e0 into main Feb 28, 2026
5 checks passed
@JacobCoffee JacobCoffee deleted the opengrph-fixes branch February 28, 2026 01:19
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds dedicated, prerendered Open Graph (OG) image endpoints for key pages and wires them into page metadata so link previews render with the updated design.

Changes:

  • Introduces new prerendered OG PNG routes for the home page, blog index, authors index, and author detail pages.
  • Refactors OG image rendering/layout by adding shared shells/components and a shared font loader.
  • Updates BaseHead and several pages to pass explicit OG image URLs into the layout/head metadata.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/pages/og/index.png.ts New prerendered OG image endpoint for the homepage.
src/pages/og/blog.png.ts New prerendered OG image endpoint for the blog index page.
src/pages/og/authors/index.png.ts New prerendered OG image endpoint for the authors index page.
src/pages/og/authors/[author].png.ts New prerendered OG image endpoint for individual author pages.
src/pages/og/[...slug].png.ts Switches post OG image route to use shared font loader.
src/pages/index.astro Passes homepage OG image into BaseLayout.
src/pages/blog/index.astro Passes blog OG image into BaseLayout.
src/pages/blog/[page].astro Passes blog OG image into paginated blog pages.
src/pages/authors/index.astro Passes authors index OG image into BaseLayout.
src/pages/authors/[author].astro Passes author OG image + description into BaseLayout.
src/lib/og-image.tsx Adds shared OG layout shell plus new page/author OG image components.
src/lib/og-fonts.ts Adds shared Inter font loader for Satori rendering.
src/components/BaseHead.astro Uses dev origin for OG image absolute URLs (while keeping prod behavior).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

return new Response(new Uint8Array(png), {
headers: {
"Content-Type": "image/png",
"Cache-Control": "public, max-age=31536000, immutable",
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The OG PNG routes are not content-hashed URLs, but they are served with Cache-Control: ... max-age=31536000, immutable. That combination can cause clients/CDNs to keep serving stale images after a deploy (e.g., when the blog post count changes). Consider removing immutable and/or using a shorter max-age, or introducing a versioned URL strategy so long-lived immutable caching is safe.

Suggested change
"Cache-Control": "public, max-age=31536000, immutable",
"Cache-Control": "public, max-age=3600",

Copilot uses AI. Check for mistakes.
Comment on lines +10 to +22
let fontsPromise: Promise<
{ name: string; data: ArrayBuffer; weight: 400 | 600 | 700; style: "normal" }[]
> | null = null;

export function getFonts() {
if (!fontsPromise) {
fontsPromise = Promise.all([
loadFont(400).then((data) => ({ name: "Inter" as const, data, weight: 400 as const, style: "normal" as const })),
loadFont(600).then((data) => ({ name: "Inter" as const, data, weight: 600 as const, style: "normal" as const })),
loadFont(700).then((data) => ({ name: "Inter" as const, data, weight: 700 as const, style: "normal" as const })),
]);
}
return fontsPromise;
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fontsPromise is declared as Promise<...> | null, so getFonts() currently returns a nullable type. Even though the implementation always initializes the promise before returning, the nullable return type can leak into callers and allow null to be passed to satori without a type error. Consider changing this to let fontsPromise: Promise<...> | undefined (or similar) and having getFonts() explicitly return Promise<...> (non-nullable).

Copilot uses AI. Check for mistakes.
const resvg = new Resvg(svg, { fitTo: { mode: "width", value: 1200 } });
const png = resvg.render().asPng();

return new Response(new Uint8Array(png), {
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resvg.render().asPng() already returns a Uint8Array; wrapping it in new Uint8Array(png) creates an extra allocation/copy. You can pass png directly to Response to avoid unnecessary work during build/prerender.

Suggested change
return new Response(new Uint8Array(png), {
return new Response(png, {

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants