<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Diaa elsayed ziada</title>
    <description>The latest articles on DEV Community by Diaa elsayed ziada (@diaa_elsayedziada_8b7f3b).</description>
    <link>https://dev.to/diaa_elsayedziada_8b7f3b</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3865956%2F6270c589-cd7f-47f7-a61d-b0d8a3ca9dfb.jpg</url>
      <title>DEV Community: Diaa elsayed ziada</title>
      <link>https://dev.to/diaa_elsayedziada_8b7f3b</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/diaa_elsayedziada_8b7f3b"/>
    <language>en</language>
    <item>
      <title>How to Add Dynamic OG Images to Your Developer Blog in 2 Minutes</title>
      <dc:creator>Diaa elsayed ziada</dc:creator>
      <pubDate>Tue, 07 Apr 2026 13:44:23 +0000</pubDate>
      <link>https://dev.to/diaa_elsayedziada_8b7f3b/how-to-add-dynamic-og-images-to-your-developer-blog-in-2-minutes-4f71</link>
      <guid>https://dev.to/diaa_elsayedziada_8b7f3b/how-to-add-dynamic-og-images-to-your-developer-blog-in-2-minutes-4f71</guid>
      <description>&lt;h2&gt;
  
  
  Your Blog Posts Deserve Better Social Previews
&lt;/h2&gt;

&lt;p&gt;You spend hours writing a technical blog post. You share it on Twitter. And the preview looks like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Home Page&lt;/strong&gt;&lt;br&gt;
yourdomain.com&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No image. No description. Just a sad, generic text preview that nobody clicks on.&lt;/p&gt;

&lt;p&gt;The fix is Open Graph meta tags — specifically &lt;code&gt;og:image&lt;/code&gt;. But if you have 50+ blog posts, you can't open Figma and design a unique image for each one. You need dynamic generation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Puppeteer/Playwright Sucks for Blog OG Images
&lt;/h2&gt;

&lt;p&gt;The "standard" approach is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Spin up a headless browser in a serverless function&lt;/li&gt;
&lt;li&gt;Load an HTML template with your post title&lt;/li&gt;
&lt;li&gt;Screenshot it&lt;/li&gt;
&lt;li&gt;Serve the PNG&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This works in theory. In practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cold boot times&lt;/strong&gt; of 3-5 seconds mean social platforms time out and cache nothing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory usage&lt;/strong&gt; of 200-500MB per invocation means expensive serverless bills&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Font loading&lt;/strong&gt; is unreliable in headless environments&lt;/li&gt;
&lt;li&gt;You end up maintaining a mini rendering pipeline for what should be a simple image&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A Better Approach: URL-Based Image Generation
&lt;/h2&gt;

&lt;p&gt;What if every blog post got a unique social preview by just adding a URL to your &lt;code&gt;og:image&lt;/code&gt; tag?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:image"&lt;/span&gt; 
  &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"https://socialcard.risero.io/generate?title=Building+a+REST+API+with+Hono&amp;amp;template=blog&amp;amp;api_key=YOUR_KEY"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That URL returns a 1200×630 PNG. No serverless function in your codebase. No headless browser. No build step. The image is edge-cached for 24 hours so it loads instantly when someone shares your link.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works Under the Hood
&lt;/h2&gt;

&lt;p&gt;Instead of using a browser to render HTML:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Satori&lt;/strong&gt; (by Vercel) — converts JSX to SVG using a custom layout engine&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;resvg-wasm&lt;/strong&gt; — rasterizes SVG to PNG in WebAssembly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare Workers&lt;/strong&gt; — runs at the edge, &amp;lt;100ms response time&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Browser-quality rendering without an actual browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Framework Integration Examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Next.js (App Router)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/blog/[slug]/page.tsx&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateMetadata&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ogImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://socialcard.risero.io/generate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;ogImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;ogImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;description&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;ogImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;template&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;ogImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api_key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;openGraph&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ogImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;630&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Astro
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;---
const { title, description } = Astro.props;
const ogUrl = `https://socialcard.risero.io/generate?title=${encodeURIComponent(title)}&lt;span class="ni"&gt;&amp;amp;template=developer&amp;amp;api_key=YOUR_KEY`;&lt;/span&gt;
---
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:image"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;{ogUrl}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Hugo
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{{ $ogUrl := printf "https://socialcard.risero.io/generate?title=%s&lt;span class="err"&gt;&amp;amp;&lt;/span&gt;template=blog&lt;span class="err"&gt;&amp;amp;&lt;/span&gt;api_key=YOUR_KEY" (urlquery .Title) }}
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:image"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"{{ $ogUrl }}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Ghost (via Code Injection)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;meta[property="og:title"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ogMeta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;meta&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;ogMeta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;property&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;og:image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;ogMeta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://socialcard.risero.io/generate?title=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;template=blog&amp;amp;api_key=YOUR_KEY`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ogMeta&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Plain HTML (any static site)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:image"&lt;/span&gt; 
  &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"https://socialcard.risero.io/generate?title=Welcome+to+My+Blog&amp;amp;template=minimal&amp;amp;api_key=YOUR_KEY"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Templates Built for Dev Content
&lt;/h2&gt;

&lt;p&gt;The API has 10 templates, but these are the ones built specifically for developer blogs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;blog&lt;/strong&gt; — title, description, author with gradient accent. Perfect for articles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;developer&lt;/strong&gt; — dark theme, monospace font, terminal aesthetics. For dev tool blogs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;changelog&lt;/strong&gt; — version badge with diff-style indicators. For release notes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;github&lt;/strong&gt; — repo card with language, stars, forks. For open source posts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;announcement&lt;/strong&gt; — launch badge with vibrant gradient. For product announcements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each supports light/dark themes and custom accent colors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Started in 2 Minutes
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Get a free API key at &lt;a href="https://socialcard.risero.io" rel="noopener noreferrer"&gt;socialcard.risero.io&lt;/a&gt; (just your email, no credit card)&lt;/li&gt;
&lt;li&gt;Try the &lt;a href="https://socialcard.risero.io/#playground" rel="noopener noreferrer"&gt;live playground&lt;/a&gt; to preview templates with your own titles&lt;/li&gt;
&lt;li&gt;Copy the URL and drop it in your &lt;code&gt;og:image&lt;/code&gt; meta tag&lt;/li&gt;
&lt;li&gt;Share a link on Twitter/LinkedIn/Slack and see the preview&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Free tier gives you 50 images/day — enough for any personal or team blog.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I built this to solve my own dev blog problem. If you try it, I'd love to hear which templates work best for your content and what's missing.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>seo</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
