Originally requested by @isaaclombardssw via YakShaver.ai 🦬
cc: @isaaclombardssw, @Marxoz, @PothieuG, @Freego1783
🟥 Watch the video (1 min 16 sec)
Url from screen share: https://www.opengraph.xyz/url?https%3A%2F%2Fwww.ssw.com.au%2Frules%2Freply-done
Consolidates #1379, #2691 and #2697 — all three describe the same gap. See History at the bottom.
Pain
Sharing any SSW Rules link (Teams, Slack, LinkedIn, X, Facebook) shows no preview image. The Next.js site emits zero Open Graph and Twitter Card meta tags — not even a generic default.
curl -sL https://www.ssw.com.au/rules/reply-done | grep -i 'og:\|twitter:'
# (no output)

❌ Figure: Bad example — no og:image, so the link falls back to a plain text card
For comparison, www.ssw.com.au sets a default on every page:
<meta property="og:image" content="https://assets.tina.io/8a7b77f2-cde8-44e7-a592-ebeb7e9dc437/ssw-default-og.jpg"/>
✅ Figure: Good example — the main SSW site has a default OG image
Cause
This is a regression from the Gatsby → Next.js migration. The old src/components/head/head.js (deleted in 3c0fd0b "removing gatsby files") set the full set — og:title, og:description, og:type, og:site_name, og:url, og:image, twitter:card=summary_large_image, twitter:site, twitter:creator, fb:app_id — defaulting the image to /rules/assets/Open-Graph-images-Website-Rules.jpg. That asset now 404s too.
Nothing replaced it:
app/layout.tsx — no openGraph, no twitter, no metadataBase
generateMetadata() in app/[filename]/page.tsx — only title, description and canonical
Suggested Solution
Two phases. Phase 1 is shippable on its own and fixes the reported pain.
Phase 1 — Default OG image (small)
Use Next's Metadata API rather than hand-rolled tags:
- Set
metadataBase + default openGraph / twitter in app/layout.tsx so every route inherits them (rules, categories, latest-rules, search, user, archived…)
- Add a default OG image (1200×630) to
public/ — or point at the existing SSW default
- In each
generateMetadata(), override openGraph.title / description / url with the page's own values
Phase 2 — Dynamic per-rule OG images (carried from #1379)
@adamcogan's original ask: a per-rule image rather than one generic one.

✅ Figure: Good example — specific thumbnail, as on https://github.com/ApacheTech/BlazOrchard
- Should include the image of the first author (People data)
- Should include some specific text from that rule (the SEO description is a good basis)
- @bradystroud: use Vercel's OG image generation rather than building our own — the site is already on Next
- Needs PO approval before starting
Tasks
Phase 1
Phase 2
Acceptance Criteria
- SSW Rules pages include valid Open Graph metadata for title, description and image
- A valid OG image is displayed when Rules links are shared on social platforms
- Metadata validates correctly in common social sharing preview tools
- (Phase 2) The OG image is generated per-rule and populated from People data
Out of scope — please don't chase these
"Malformed SEO title" is a false positive. The original report flagged quote characters in the title. The raw HTML is correct:
<title>Dones - Do you reply 'Done' and delete the original email? | SSW.Rules</title>
' is a properly escaped apostrophe. React escapes it, and any conformant HTML parser decodes it back to '. opengraph.xyz simply doesn't decode entities in its preview pane — that's a display bug on their end, not ours. Adding an explicit og:title won't change the escaping either, since Next escapes attribute content the same way. No title sanitisation is needed.
Title length — separate nit worth its own PBI: the reply-done title is 80 characters, over Google's ~60 char truncation point. Not an OG problem.
History
| Issue |
Filed |
Covered |
| #1379 |
Jun 2024 |
Dynamic per-rule OG images → now Phase 2 |
| #2691 |
Jul 2026 |
Missing default OG image + root cause → now Phase 1 + Cause |
| #2697 |
Aug 2026 |
Missing OG metadata, video repro, ACs → now Pain + AC |
Originally requested by @isaaclombardssw via YakShaver.ai 🦬
cc: @isaaclombardssw, @Marxoz, @PothieuG, @Freego1783
🟥 Watch the video (1 min 16 sec)
Url from screen share: https://www.opengraph.xyz/url?https%3A%2F%2Fwww.ssw.com.au%2Frules%2Freply-done
Pain
Sharing any SSW Rules link (Teams, Slack, LinkedIn, X, Facebook) shows no preview image. The Next.js site emits zero Open Graph and Twitter Card meta tags — not even a generic default.
❌ Figure: Bad example — no og:image, so the link falls back to a plain text card
For comparison, www.ssw.com.au sets a default on every page:
✅ Figure: Good example — the main SSW site has a default OG image
Cause
This is a regression from the Gatsby → Next.js migration. The old
src/components/head/head.js(deleted in3c0fd0b"removing gatsby files") set the full set —og:title,og:description,og:type,og:site_name,og:url,og:image,twitter:card=summary_large_image,twitter:site,twitter:creator,fb:app_id— defaulting the image to/rules/assets/Open-Graph-images-Website-Rules.jpg. That asset now 404s too.Nothing replaced it:
app/layout.tsx— noopenGraph, notwitter, nometadataBasegenerateMetadata()inapp/[filename]/page.tsx— onlytitle,descriptionandcanonicalSuggested Solution
Two phases. Phase 1 is shippable on its own and fixes the reported pain.
Phase 1 — Default OG image (small)
Use Next's
MetadataAPI rather than hand-rolled tags:metadataBase+ defaultopenGraph/twitterinapp/layout.tsxso every route inherits them (rules, categories, latest-rules, search, user, archived…)public/— or point at the existing SSW defaultgenerateMetadata(), overrideopenGraph.title/description/urlwith the page's own valuesPhase 2 — Dynamic per-rule OG images (carried from #1379)
@adamcogan's original ask: a per-rule image rather than one generic one.
✅ Figure: Good example — specific thumbnail, as on https://github.com/ApacheTech/BlazOrchard
Tasks
Phase 1
metadataBaseand defaultopenGraph+twittertoapp/layout.tsxopenGraphin thegenerateMetadata()functions (rules + categories first)Phase 2
@vercel/og, using first author + SEO descriptionAcceptance Criteria
Out of scope — please don't chase these
"Malformed SEO title" is a false positive. The original report flagged quote characters in the title. The raw HTML is correct:
'is a properly escaped apostrophe. React escapes it, and any conformant HTML parser decodes it back to'. opengraph.xyz simply doesn't decode entities in its preview pane — that's a display bug on their end, not ours. Adding an explicitog:titlewon't change the escaping either, since Next escapes attribute content the same way. No title sanitisation is needed.Title length — separate nit worth its own PBI: the
reply-donetitle is 80 characters, over Google's ~60 char truncation point. Not an OG problem.History