Skip to content

fix(og): read OG fonts from the repo instead of fetching Google Fonts at build - #6471

Merged
waleedlatif1 merged 2 commits into
stagingfrom
fix/og-font-build-dependency
Aug 9, 2026
Merged

fix(og): read OG fonts from the repo instead of fetching Google Fonts at build#6471
waleedlatif1 merged 2 commits into
stagingfrom
fix/og-font-build-dependency

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • The release build died prerendering an integration OG card with No fonts are loaded. At least one font is required to calculate the layout.
  • loadGoogleFont swallowed every failure and returned null, so a throttled fetch produced an empty fonts array — and Satori requires at least one.
  • Six routes build OG images (integrations/[slug] alone is 237 pages), and each render fetched two weights subsetted by &text= — a per-page URL no cache can reuse. Several hundred uncacheable requests to one host from one CI egress IP across parallel build workers, so a page losing that race was expected, not unlucky. Mintlify was just whichever page drew the short straw — its description is 56 chars, unremarkable next to slack's 141.
  • Geist 400/500 now ship in public/brand/fonts and are read once at module scope, per Next's ImageResponse guidance. .ttf because Satori accepts only ttf/otf/woff — the .woff2 already served to browsers cannot be reused here.
  • public/ needs no outputFileTracingIncludes entry: docker/app.Dockerfile copies it into the runner, which the force-dynamic share-token card needs since it renders per request.

Output is unchanged

Rendered the same card with the full font and with the old Google subset and diffed the pixels:

DIMS 1200x630 | differing subpixels: 0/3024000 (0.0000%) | max channel delta: 0
byte-identical PNG: true

Performance

before after
render ~74ms + ~425ms font fetch ~74ms
network per render 4 requests to Google 0
share card (force-dynamic) 2 Google round trips per request 0

Font cost is 146 KB for both weights, against ImageResponse's 500 KB budget.

Type of Change

  • Bug fix

Testing

Added og-utils.test.tsx, which renders a real PNG and asserts the magic number — it fails with a loud ENOENT if a font file goes missing, which is the right failure mode: deterministic, at build time, naming the file. Verified by deleting a font and watching it fail. bun run type-check clean; landing suite 6/6.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

… at build

The release build died prerendering an integration OG card with "No fonts are
loaded. At least one font is required to calculate the layout."

loadGoogleFont swallowed every failure and returned null, so a throttled fetch
produced an empty fonts array, and Satori requires at least one. Six routes
build OG images -- integrations/[slug] alone is 237 pages -- and each render
fetched two weights subsetted by &text=, a per-page URL no cache can reuse.
Several hundred uncacheable requests to one host from one CI egress IP across
parallel build workers, so a page losing that race was expected, not unlucky.
Mintlify was just whichever page drew the short straw; its description is 56
chars, unremarkable next to slack's 141.

Geist 400/500 now ship in public/brand/fonts and are read once at module scope,
per Next's ImageResponse guidance. .ttf because Satori accepts only ttf/otf/woff
-- the .woff2 already served to browsers cannot be reused. public/ needs no
outputFileTracingIncludes entry: the Dockerfile copies it into the runner, which
the force-dynamic share-token card needs since it renders per request.

Output is unchanged: rendering the same card with the full font and with the old
subset produces a byte-identical PNG (0 of 3,024,000 subpixels differ). Render
drops from ~74ms plus ~425ms of font fetching to ~74ms, and the share card no
longer makes two Google round trips per request.
@vercel

vercel Bot commented Aug 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Aug 9, 2026 2:10am

Request Review

@cursor

cursor Bot commented Aug 9, 2026

Copy link
Copy Markdown

PR Summary

Low Risk
Build-time OG rendering and asset bundling only; visual output is intended to be pixel-identical to the previous Google subset approach.

Overview
Landing OG images (createLandingOgImage) no longer fetch per-page Geist subsets from Google Fonts at build or request time. Geist 400/500 .ttf files are vendored under public/brand/fonts, read once at module load, and always passed to ImageResponse—fixing flaky "No fonts are loaded" build failures when fetches returned empty arrays.

Adds Geist OFL license text and a Vitest test that renders a real PNG and checks the PNG magic bytes so missing fonts fail loudly at test time.

Reviewed by Cursor Bugbot for commit 6ba48b8. Configure here.

@greptile-apps

greptile-apps Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR replaces per-render Google Fonts requests for OG images with repository-bundled Geist fonts loaded once at module initialization.

  • Adds regular and medium Geist TTF assets and their license.
  • Updates the shared landing-page OG renderer to use the bundled font buffers.
  • Adds a real PNG rendering test that verifies the font-backed image pipeline.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
apps/sim/app/(landing)/og-utils.tsx Replaces fallible remote font fetching with deterministic module-scope reads of bundled Geist fonts.
apps/sim/app/(landing)/og-utils.test.tsx Exercises the real OG renderer and verifies that it returns a nontrivial PNG.
apps/sim/public/brand/fonts/Geist-Regular.ttf Adds the bundled Geist regular font used by the OG renderer.
apps/sim/public/brand/fonts/Geist-Medium.ttf Adds the bundled Geist medium font used by the OG renderer.
apps/sim/public/brand/fonts/Geist-OFL.txt Adds the license accompanying the bundled Geist font files.

Reviews (2): Last reviewed commit: "docs(og): record why process.cwd() is th..." | Re-trigger Greptile

Comment thread apps/sim/app/(landing)/og-utils.tsx
Review flagged the font path as invalid in the standalone image, reasoning
that the container starts at the monorepo root. It does -- but Next's generated
standalone server.js opens with process.chdir(__dirname), and that file ships
beside public/. Same reason content/ is read this way at runtime.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 6ba48b8. Configure here.

@waleedlatif1
waleedlatif1 merged commit 64fb8f0 into staging Aug 9, 2026
30 checks passed
@waleedlatif1
waleedlatif1 deleted the fix/og-font-build-dependency branch August 9, 2026 02:14
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.

1 participant