• Blog
  • Docs
  • Pricing
  • We’re hiring!
Log inSign up
alexwein

alexwein

alexBlogs

me writing about things maybe
Remix of stevekrouse/markdownBlogStarter
Public
Like
alexBlogs
Home
Code
11
components
1
other-posts
2
posts
9
utils
1
Layout.tsx
README.md
about.md
analytics.ts
H
index.tsx
prompt.md
robots.txt
Environment variables
1
Branches
7
Pull requests
Remixes
1
History
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Sign up now
Code
/
prompt.md
Code
/
prompt.md
Search
2/7/2026
Viewing readonly version of og-images branch: v15
View latest version
prompt.md

Open Graph Images Implementation Prompt for Townie

Goal

Add Open Graph (OG) meta tags to this blog so posts display rich previews when shared on social media (Twitter, Facebook, LinkedIn, Slack, etc.). Each page should have a dynamically generated OG image.

Reference Implementation

Study how valdottown/blog implements OG images:

  • components/OGImage.tsx - SVG component that renders the OG image with title text
  • routes/og-image.tsx - HTTP route that renders SVG to PNG using resvg_wasm
  • components/Head.tsx - Adds OG meta tags to the HTML head, constructing image URL with title query param

Requirements

1. Create an OG Image Component (components/OGImage.tsx)

Create an SVG-based React component similar to the reference:

  • Default title: "alex wein's blog"
  • For individual posts, show the post title with "alex wein's blog" displayed smaller (similar to how Val Town Blog shows "Blog" at the bottom)
  • Use word-wrap package to handle long titles across multiple lines
  • Simple, clean design (white background, black text)
  • Standard OG image dimensions: 1200x640
Create val
/** @jsxImportSource https://esm.sh/react@18.2.0 */ import wrap from "https://esm.sh/word-wrap@1.2.5"; export default function OGImage({ title = "alex wein's blog" }: { title?: string }) { // Implementation here }

2. Create an OG Image Route

Add a route handler for /og-image.png that:

  • Accepts a ?title= query parameter
  • Renders the OGImage component to SVG using renderToStaticMarkup
  • Converts SVG to PNG using resvg_wasm:
    Create val
    import { render as renderPNG } from "https://deno.land/x/resvg_wasm@0.2.0/mod.ts";
  • Returns PNG with proper Content-Type: image/png header
  • Optionally support ?format=svg for debugging

3. Extract Post Titles from Markdown

Important: This blog does NOT use front-matter. Instead, extract the title from the first ## or # heading in each markdown file.

Create a utility function to extract the title:

Create val
function extractTitleFromMarkdown(markdown: string): string | null { // Match first h1 or h2 const match = markdown.match(/^#{1,2}\s+(.+)$/m); return match ? match[1] : null; }

4. Update Layout.tsx to Accept OG Meta Props

Modify Layout.tsx to accept and render OG meta tags:

  • og:title - page title
  • og:description - optional description
  • og:image - URL to the OG image endpoint with title param
  • og:type - "website" or "article"
  • twitter:card - "summary_large_image"
  • twitter:title, twitter:image

Example:

Create val
export function Layout({ children, title = "alex wein's blog", description, ogImageTitle }: { children: ReactNode; title?: string; description?: string; ogImageTitle?: string; }) { const baseUrl = "https://alexwein-alexblogs.web.val.run"; // Update with actual URL const ogImageUrl = `${baseUrl}/og-image.png?title=${encodeURIComponent(ogImageTitle || title)}`; return ( <html lang="en"> <head> {/* existing meta tags */} <meta property="og:title" content={title} /> <meta property="og:image" content={ogImageUrl} /> <meta property="twitter:card" content="summary_large_image" /> <meta property="twitter:title" content={title} /> <meta property="twitter:image" content={ogImageUrl} /> {description && <meta property="og:description" content={description} />} </head> <body>{children}</body> </html> ); }

5. Update index.tsx Routes

Modify each route to:

  1. Extract the post title from markdown content
  2. Pass the title to the Layout component
  3. Add the /og-image.png route handler

For the homepage (/):

  • Use default title "alex wein's blog"

For individual posts (/posts/:slug):

  • Extract title from the markdown's first h1/h2
  • Pass to Layout for OG tags
  • OG image should show post title with "alex wein's blog" as subtitle

For special pages (about, dbt, visualizer):

  • Use appropriate page-specific titles

File Structure After Implementation

alexBlogs/
β”œβ”€β”€ components/
β”‚   └── OGImage.tsx          # NEW: SVG OG image component
β”œβ”€β”€ utils/
β”‚   └── extractTitle.ts      # NEW: Extract title from markdown
β”œβ”€β”€ Layout.tsx               # MODIFIED: Add OG meta tag support
β”œβ”€β”€ index.tsx                # MODIFIED: Add /og-image.png route, pass titles
β”œβ”€β”€ posts/
β”‚   └── *.md
└── ...

Testing

After implementation:

  1. Visit /og-image.png - should show default "alex wein's blog" image
  2. Visit /og-image.png?title=Test%20Title - should show custom title
  3. Visit /og-image.png?format=svg - should return SVG for debugging
  4. Use a social media debugger (Facebook Sharing Debugger, Twitter Card Validator) to verify meta tags
  5. Check that each blog post page has unique OG image with its title

Notes

  • The current posts use ## (h2) for titles, not # (h1). Handle both cases.
  • Keep the implementation simple - no need for custom fonts or complex styling initially
  • The base URL will need to be determined from the request or hardcoded for now
  • Consider caching the PNG renders if performance becomes an issue
FeaturesVersion controlCode intelligenceCLIMCP
Use cases
TeamsAI agentsSlackGTM
DocsShowcaseTemplatesNewestTrendingAPI examplesNPM packages
PricingNewsletterBlogAboutCareers
We’re hiring!
Brandhi@val.townStatus
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Open Source Pledge
Terms of usePrivacy policyAbuse contact
Β© 2026 Val Town, Inc.