Add Open Graph (OG) meta tags to this blog for better social media sharing. When a link to the blog is shared on Twitter, Discord, Slack, etc., it should show a nice preview image with the post title.
See how valdottown/blog implements OG images:
components/Head.tsx- The<head>component with OG meta tagscomponents/OGImage.tsx- SVG component that renders the OG imageroutes/og-image.tsx- HTTP endpoint that serves the OG image as PNG
alexwein/alexBlogs (branch: og-images-2)
βββ index.tsx # Main HTTP handler with all routes
βββ Layout.tsx # HTML wrapper (currently minimal <head>)
βββ analytics.ts # Hit tracking
βββ about.md # About page content
βββ posts/ # Blog posts (markdown files)
β βββ 2025-12-25-chart-games-2025.md
β βββ 2025-12-24-five-friendly-sql-tips.md
β βββ ... (more posts)
βββ other-posts/ # Special posts (like dvs-spin.md)
βββ robots.txt
Create an SVG component that generates the OG image. Follow the reference blog's approach:
- Use SVG (1200x640 viewport) for the image
- Display the post title as the main text (with word wrapping using
word-wrappackage) - Below the title, display "alex wein's blog" as the site name (similar to how the reference shows "Blog")
- Use a simple, clean design with a horizontal line separator
- Keep the font styling simple (sans-serif) since custom fonts are tricky in SVG-to-PNG conversion
Example structure:
/** @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 }) {
// Word wrap the title
// Return SVG with title text and "alex wein's blog" footer
}
Create an HTTP endpoint that:
- Accepts a
?title=query parameter - Renders the OGImage component to SVG
- Converts SVG to PNG using
https://deno.land/x/resvg_wasm@0.2.0/mod.ts - Returns the PNG with appropriate Content-Type headers
The route should be accessible at /og-image.png.
Create a reusable Head component that includes:
- Basic meta tags (charset, viewport)
- Page
<title>(with format:{postTitle} | alex wein's blogor justalex wein's blogfor homepage) - OG meta tags:
og:type= "website"og:title= the page titleog:description= (optional, can be empty or a default)og:image= URL to/og-image.png?title={encodedTitle}
- Twitter card meta tags:
twitter:card= "summary_large_image"twitter:title= the page titletwitter:image= same as og:image
Props should include:
title: string- The page/post title
Modify Layout to accept and use the Head component:
- Add a
titleprop - Replace the existing
<head>contents with the new Head component - Keep the existing stylesheet link
Update all routes to pass the appropriate title:
- Title:
"alex wein's blog"
- Extract the title from the markdown content by finding the first
##or#heading - Pass this extracted title to Layout
- Title:
"About"
- Extract title from markdown or use a sensible default
Create a helper function to extract the title from markdown:
function extractTitleFromMarkdown(markdown: string): string {
// Look for first h1 (# ) or h2 (## ) heading
const h1Match = markdown.match(/^#\s+(.+)$/m);
const h2Match = markdown.match(/^##\s+(.+)$/m);
// Prefer h1, fall back to h2, then default
return h1Match?.[1] || h2Match?.[1] || "alex wein's blog";
}
Note: The current posts use ## (h2) as their top-level headings, so make sure to check for both # and ##.
You'll need to determine the base URL for constructing absolute OG image URLs. Options:
- Hardcode it (e.g.,
https://alexwein-alexblogs.web.val.run) - Derive it from
req.urlin the handler - Use a constant file like the reference does
The /og-image.png route needs to be added to the main handler in index.tsx. Add it before the 404 fallback:
if (url.pathname === "/og-image.png") {
// Import and use the og-image handler
}
Or create it as a sub-app using Hono like the reference does.
https://esm.sh/word-wrap@1.2.5- For wrapping long titleshttps://deno.land/x/resvg_wasm@0.2.0/mod.ts- For SVG to PNG conversion
Keep the OG image design simple and readable:
- White background
- Black text
- A colored horizontal line as a separator (pick a nice accent color)
- The post title large and prominent
- "alex wein's blog" smaller, at the bottom
| File | Action |
|---|---|
components/OGImage.tsx | Create - SVG image component |
components/Head.tsx | Create - Head component with OG tags |
og-image.tsx or routes/og-image.tsx | Create - PNG endpoint |
Layout.tsx | Modify - Use Head component, accept title prop |
index.tsx | Modify - Extract titles, pass to Layout, add og-image route |
After implementation, test by:
- Visiting
/og-image.png- should show default "alex wein's blog" image - Visiting
/og-image.png?title=Test%20Title- should show "Test Title" - Using a social media debugger (Twitter Card Validator, Facebook Sharing Debugger, etc.) to verify the OG tags are working