feat: branded dynamic OG/social cards via @vercel/og#1334
Conversation
Replace the single Lato/planet OG card with the relaunch design system:
five Satori-safe templates (main, post, profile, publication, job) served
from one `/og` route, wired into every shareable page from real DB models.
- lib/og/{tokens,fonts,templates}.ts(x): design tokens, Google-font loader
(edge), and the card components. app/og/route.tsx dispatches on `type` and
embeds the white wordmark (public/og/wordmark-white.png) as a cached data
URI so Satori never resolves a same-origin <img> mid-render.
- lib/og/url.ts: typed builders (one per card) that map our models to the
route's params, cap tags, and append a `v=<updatedAt>` cache-buster.
- utils/hue.ts: extract the deterministic per-string hue (was inline in the
source-profile client) so OG avatars/marks match on-site tints.
- Add openGraph.images + twitter (summary_large_image) to home/fallback,
about, advertise, discussions, jobs, tag, speakers, volunteer, and the
dynamic article/discussion/link/profile/publication/job pages. JSON-LD
article image uses the same builder.
- Long immutable cache-control on the route; runtime stays edge.
Verified: all card types render 1200x630 PNGs and og:image resolves on
real pages.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (22)
WalkthroughIntroduces a new ChangesDynamic OG Image System
Sequence Diagram(s)sequenceDiagram
participant PageServer as Page generateMetadata
participant UrlBuilder as lib/og/url.ts builders
participant OgRoute as /og Edge Route
participant FontCache as coduFonts() cache
participant LogoCache as logo() cache
participant OgImage as OgImage dispatcher
participant Satori as ImageResponse/Satori
PageServer->>UrlBuilder: ogPostImage / ogProfileImage / ogJobImage / ogMainImage
UrlBuilder-->>PageServer: /og?type=X&... URL string
Note over PageServer: URL embedded in metadata openGraph/twitter images
rect rgba(100, 149, 237, 0.5)
Note over OgRoute,Satori: At social preview fetch time
OgRoute->>FontCache: await coduFonts()
OgRoute->>LogoCache: await logo(origin)
OgRoute->>OgImage: OgImage(OgParams { type, ...fields })
OgImage-->>OgRoute: React element (MainCard/PostCard/ProfileCard/etc.)
OgRoute->>Satori: new ImageResponse(element, { fonts, size })
Satori-->>OgRoute: PNG bytes
OgRoute-->>PageServer: 200 image/png (immutable 1yr cache)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
What
Replaces the single Lato/planet OG card with the relaunch design-system social cards. Five Satori-safe templates — main, post, profile, publication, job — served from one
/ogroute and wired into every shareable page from real DB models.Changes
Core (
lib/og/)tokens.ts/fonts.ts/templates.tsx— design tokens, edge Google-font loader (Bricolage / Hanken / JetBrains Mono), and the card components (from the design handoff).app/og/route.tsx— singleGET /oghandler, dispatches ontype. Embeds the white wordmark (public/og/wordmark-white.png) as a cached data URI so Satori never resolves a same-origin<img>mid-render (flaky in dev, a round-trip in prod). Longimmutablecache-control; runtime staysedge.lib/og/url.ts— typed builders (one per card) that map our models to the route's exact params, cap tags (2 posts / 3 job·interests), and appendv=<updatedAt>so edited records get a fresh card.utils/hue.ts— extracted the deterministic per-string hue (was inline in the source-profile client, now shared) so OG avatars/marks match on-site tints.Page wiring —
openGraph.images+twitter(summary_large_image) on:/about,/advertise,/discussions,/jobs,/tag/[slug],/speakers,/volunteer/[username]/[slug](article + member-link),/d/[slug](discussion),/s/[sourceSlug]/[slug](curated link),/s/[sourceSlug](publication),/[username](profile),/jobs/[slug](job)imageuses the same builder.Notes / judgement calls
/letters/[slug](MDX, no metadata), not a/weeklyindex./articlesis a 301 redirect, so it's skipped.kind=linkmapping.ReaderPosttype doesn't carryjobTitle; kept the change minimal rather than widening it).display:flex;width:autoon the wordmark<img>) — required for the cards to render.Verification
All card types render 1200×630 PNGs;
og:image+twitter:imageresolve tohttps://www.codu.co/og?type=…on real pages (checked/about,/jobs). Typecheck + lint clean; structured-data unit tests pass.🤖 Generated with Claude Code