<?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: Corbanware</title>
    <description>The latest articles on DEV Community by Corbanware (@corbanware).</description>
    <link>https://dev.to/corbanware</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%2F3773444%2Faf1e64c2-072e-4647-b4df-a2b0da0dc2fc.jpg</url>
      <title>DEV Community: Corbanware</title>
      <link>https://dev.to/corbanware</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/corbanware"/>
    <language>en</language>
    <item>
      <title>I Built a Free OG Image API -- Here's What I Learned</title>
      <dc:creator>Corbanware</dc:creator>
      <pubDate>Mon, 18 May 2026 01:01:25 +0000</pubDate>
      <link>https://dev.to/corbanware/i-built-a-free-og-image-api-heres-what-i-learned-c3d</link>
      <guid>https://dev.to/corbanware/i-built-a-free-og-image-api-heres-what-i-learned-c3d</guid>
      <description>&lt;h1&gt;
  
  
  I Built a Free OG Image API -- Here's What I Learned
&lt;/h1&gt;

&lt;p&gt;Six weeks ago, I was in Figma at 11 PM on a Tuesday, designing my fourteenth OG image of the week. Same process as always: create a 1200x630 canvas, add a gradient background, type the blog post title, adjust the font size until it fits, export as PNG, upload to my project, update the meta tags. Fifteen minutes per image. Fourteen images. Three and a half hours of my life that week, doing something a computer should be doing.&lt;/p&gt;

&lt;p&gt;That was the moment I decided to build &lt;a href="https://ogimg.xyz?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=wf5" rel="noopener noreferrer"&gt;ogimg.xyz&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem That Would Not Go Away
&lt;/h2&gt;

&lt;p&gt;I am a solo developer building a portfolio of small SaaS tools. Every product I ship needs a landing page. Every landing page needs OG images. Every blog post needs an OG image. Every documentation page needs one too.&lt;/p&gt;

&lt;p&gt;At first, I used Figma templates. It worked for the first 10 images. By image 30, I was cutting corners. By image 50, I was skipping OG images entirely.&lt;/p&gt;

&lt;p&gt;I looked at existing solutions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Headless browser screenshot services&lt;/strong&gt; -- Slow (1-3 seconds per image) and expensive to run at scale.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Design tool APIs&lt;/strong&gt; -- Pricing starts at $40-50/month. Hard to justify for a solo dev with no revenue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;@vercel/og&lt;/strong&gt; -- Excellent, but I did not want to build template code in every project. I wanted a single API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these hit the sweet spot: fast, cheap (ideally free for small usage), and an API callable from any project.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Technical Decisions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Vercel Edge, Not a Server
&lt;/h3&gt;

&lt;p&gt;The single most important decision was running on Vercel Edge Functions instead of a traditional server. Edge functions execute on Cloudflare's network, close to the user, with near-zero cold starts.&lt;/p&gt;

&lt;p&gt;The result: most requests complete in 200-500ms. Fast enough to use as a dynamic og:image URL.&lt;/p&gt;

&lt;h3&gt;
  
  
  No Headless Browser
&lt;/h3&gt;

&lt;p&gt;I used &lt;strong&gt;Satori&lt;/strong&gt;, Vercel's library that converts React JSX directly to SVG. The SVG then gets rasterized to PNG via &lt;strong&gt;Resvg&lt;/strong&gt;, a Rust-based SVG renderer compiled to WebAssembly.&lt;/p&gt;

&lt;p&gt;The pipeline: JSX -&amp;gt; SVG -&amp;gt; PNG. No browser. No DOM. 100-200ms total.&lt;/p&gt;

&lt;p&gt;The tradeoff? Limited CSS subset. No position: absolute. No box-shadow. No CSS Grid. Everything Flexbox. This constraint turned out to be a feature -- it forced simpler, cleaner templates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Template-Based, Not Freeform
&lt;/h3&gt;

&lt;p&gt;I chose curated templates over arbitrary HTML/CSS:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Performance.&lt;/strong&gt; Pre-built templates mean predictable rendering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quality floor.&lt;/strong&gt; Every image looks decent by default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity.&lt;/strong&gt; Users send template=gradient instead of writing JSX.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;10 templates covering gradient backgrounds, minimal text, bold hero-style, documentation, corporate, retro, and more. Each supports 10 background patterns (dots, grid, diagonal lines, waves, hexagons, etc.).&lt;/p&gt;

&lt;h2&gt;
  
  
  What Surprised Me
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Simplicity Wins, Every Time
&lt;/h3&gt;

&lt;p&gt;My initial version had 25 configurable parameters. Nobody used most of them. I stripped it down to about 10 that actually matter: title, description, template, pattern, background color, text color, accent color, site name, author, and logo. Usage immediately went up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Free Tier Drives Adoption
&lt;/h3&gt;

&lt;p&gt;I offer a free tier: 50 images per month, 4 templates. No credit card required.&lt;/p&gt;

&lt;p&gt;Developers are allergic to entering payment information before evaluating a tool. The free tier lets people integrate, see it working, then decide to upgrade.&lt;/p&gt;

&lt;h3&gt;
  
  
  Edge Rendering Is the Right Architecture
&lt;/h3&gt;

&lt;p&gt;OG images are generated when a social platform's crawler visits a URL. That crawler could be anywhere geographically. Edge rendering means the image is generated at the nearest PoP, so a Facebook crawler in Ireland does not wait for a round trip to us-east-1.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Honest Numbers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Development time:&lt;/strong&gt; About 6 weeks, nights and weekends&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure cost:&lt;/strong&gt; Effectively $0/month. Vercel free tier + Neon PostgreSQL free tier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Revenue:&lt;/strong&gt; Early days. A few paid users on Hobby ($4.90/month). One lifetime purchase ($149).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is product number one in a larger portfolio. The goal was to build something useful, learn the full stack of launching a SaaS, and have a tool I genuinely use myself.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Would Do Differently
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ship the MVP faster.&lt;/strong&gt; I spent too long polishing templates before launch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build the visual playground earlier.&lt;/strong&gt; The interactive playground turned out to be the primary way people discover the product.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write integration guides from the start.&lt;/strong&gt; Developers search for 'Next.js OG images', not 'OG image generation API'.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;If any of this resonates, give &lt;a href="https://ogimg.xyz?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=wf5" rel="noopener noreferrer"&gt;ogimg.xyz&lt;/a&gt; a try. Free tier, no credit card:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://ogimg.xyz/api/og?title=Hello+World&amp;amp;template=gradient"&lt;/span&gt;&amp;amp;utm_source&lt;span class="o"&gt;=&lt;/span&gt;devto&amp;amp;utm_medium&lt;span class="o"&gt;=&lt;/span&gt;article&amp;amp;utm_campaign&lt;span class="o"&gt;=&lt;/span&gt;wf5 &lt;span class="nt"&gt;--output&lt;/span&gt; og-image.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or visit the site and use the visual playground to design something, then copy the URL for your og:image meta tag.&lt;/p&gt;

&lt;p&gt;I am building this in public as a solo developer. If you have feedback, I genuinely want to hear it. Drop a comment below.&lt;/p&gt;

&lt;p&gt;Thanks for reading. Now go add OG images to that side project you have been meaning to polish.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you found this interesting, consider leaving a reaction. Building in public is more fun when people are actually watching.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>startup</category>
      <category>saas</category>
      <category>showdev</category>
    </item>
    <item>
      <title>OG Image Sizes: The Complete Guide for 2026</title>
      <dc:creator>Corbanware</dc:creator>
      <pubDate>Mon, 11 May 2026 01:00:58 +0000</pubDate>
      <link>https://dev.to/corbanware/og-image-sizes-the-complete-guide-for-2026-4l6d</link>
      <guid>https://dev.to/corbanware/og-image-sizes-the-complete-guide-for-2026-4l6d</guid>
      <description>&lt;h1&gt;
  
  
  OG Image Sizes: The Complete Guide for 2026
&lt;/h1&gt;

&lt;p&gt;Here is the quick answer: &lt;strong&gt;1200 x 630 pixels&lt;/strong&gt;. If you remember nothing else from this article, remember that number. It works on every major platform without cropping or distortion.&lt;/p&gt;

&lt;p&gt;But if you want to understand why, what the edge cases are, and how to avoid the mistakes that make your shared links look broken, keep reading.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Recommended Size&lt;/th&gt;
&lt;th&gt;Min Size&lt;/th&gt;
&lt;th&gt;Aspect Ratio&lt;/th&gt;
&lt;th&gt;Max File Size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Facebook&lt;/td&gt;
&lt;td&gt;1200 x 630&lt;/td&gt;
&lt;td&gt;600 x 315&lt;/td&gt;
&lt;td&gt;1.91:1&lt;/td&gt;
&lt;td&gt;8 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Twitter/X (large card)&lt;/td&gt;
&lt;td&gt;1200 x 628&lt;/td&gt;
&lt;td&gt;300 x 157&lt;/td&gt;
&lt;td&gt;2:1&lt;/td&gt;
&lt;td&gt;5 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Twitter/X (summary)&lt;/td&gt;
&lt;td&gt;240 x 240&lt;/td&gt;
&lt;td&gt;144 x 144&lt;/td&gt;
&lt;td&gt;1:1&lt;/td&gt;
&lt;td&gt;5 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LinkedIn&lt;/td&gt;
&lt;td&gt;1200 x 627&lt;/td&gt;
&lt;td&gt;200 x 200&lt;/td&gt;
&lt;td&gt;1.91:1&lt;/td&gt;
&lt;td&gt;5 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slack&lt;/td&gt;
&lt;td&gt;1200 x 630&lt;/td&gt;
&lt;td&gt;Any&lt;/td&gt;
&lt;td&gt;1.91:1&lt;/td&gt;
&lt;td&gt;No hard limit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Discord&lt;/td&gt;
&lt;td&gt;1200 x 630&lt;/td&gt;
&lt;td&gt;Any&lt;/td&gt;
&lt;td&gt;1.91:1&lt;/td&gt;
&lt;td&gt;8 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WhatsApp&lt;/td&gt;
&lt;td&gt;1200 x 630&lt;/td&gt;
&lt;td&gt;300 x 200&lt;/td&gt;
&lt;td&gt;1.91:1&lt;/td&gt;
&lt;td&gt;5 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iMessage&lt;/td&gt;
&lt;td&gt;1200 x 630&lt;/td&gt;
&lt;td&gt;Any&lt;/td&gt;
&lt;td&gt;1.91:1&lt;/td&gt;
&lt;td&gt;No hard limit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pinterest&lt;/td&gt;
&lt;td&gt;1200 x 630&lt;/td&gt;
&lt;td&gt;200 x 200&lt;/td&gt;
&lt;td&gt;1.91:1&lt;/td&gt;
&lt;td&gt;10 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Bookmark this table. Reference it when you need it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why 1200 x 630 Is the Standard
&lt;/h2&gt;

&lt;p&gt;Back in 2010, Facebook introduced the Open Graph protocol to let websites control how shared links appear in the News Feed. They settled on a roughly 1.91:1 aspect ratio for link preview images. Every other platform that came along afterward -- Twitter, LinkedIn, Slack, Discord -- adopted the same ratio or something close enough that a 1200x630 image displays without issues.&lt;/p&gt;

&lt;p&gt;The result: one image at 1200 x 630 works everywhere. You do not need platform-specific images. Period.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why 1200px wide specifically?&lt;/strong&gt; Because it renders crisply on high-DPI (Retina) displays. On a standard feed where link cards are displayed at around 600px wide, a 1200px source image means 2x pixel density. Your text stays sharp, your gradients stay smooth.&lt;/p&gt;

&lt;h2&gt;
  
  
  PNG vs. JPEG: Which Format to Use
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PNG&lt;/strong&gt; -- Use for images with text, solid colors, or sharp edges. Most OG images fall into this category.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JPEG&lt;/strong&gt; -- Use for photographic images. JPEG compresses these more efficiently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebP&lt;/strong&gt; -- Inconsistent support across platforms. Stick to PNG or JPEG.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SVG&lt;/strong&gt; -- Do not use SVG for OG images. Most platforms will silently ignore it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Safe Zone: Where to Place Your Content
&lt;/h2&gt;

&lt;p&gt;Platforms do not all crop identically. Keep all important content within a &lt;strong&gt;60px margin&lt;/strong&gt; safe zone on all sides. That means your actual content area is roughly 1080 x 510 pixels.&lt;/p&gt;

&lt;h2&gt;
  
  
  Font Size Matters More Than You Think
&lt;/h2&gt;

&lt;p&gt;OG images are typically displayed at 400-600 pixels wide in feeds. Rules of thumb:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Title text:&lt;/strong&gt; Minimum 48px at 1200px width&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description text:&lt;/strong&gt; Minimum 28px at 1200px width&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limit text to 2-3 lines&lt;/strong&gt; for the title&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High contrast:&lt;/strong&gt; White text on dark backgrounds or vice versa&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Mistakes (and How to Fix Them)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Using a square image&lt;/strong&gt; -- A 1:1 image will get aggressively cropped. Always use 1200 x 630.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. File size too large&lt;/strong&gt; -- Aim for under 1 MB. Use TinyPNG or ImageOptim to compress.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Relative URLs in the meta tag&lt;/strong&gt; -- Social platform crawlers need the full absolute URL starting with https://.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Caching issues&lt;/strong&gt; -- Platforms cache OG images aggressively. Use each platform's debugger tool to bust the cache.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Missing width and height meta tags&lt;/strong&gt; -- Providing og:image:width and og:image:height helps platforms render previews faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Test Your OG Images
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Facebook Sharing Debugger&lt;/strong&gt; -- Paste your URL, click Scrape Again&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Twitter Card Validator&lt;/strong&gt; -- Preview the card rendering&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LinkedIn Post Inspector&lt;/strong&gt; -- Check LinkedIn's cache&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;opengraph.xyz&lt;/strong&gt; -- Previews across multiple platforms simultaneously&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Automating OG Image Generation
&lt;/h2&gt;

&lt;p&gt;For sites with many pages, manual OG image creation does not scale. Options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;@vercel/og&lt;/strong&gt; -- JSX to PNG on the edge. Free, self-hosted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudinary&lt;/strong&gt; -- Text overlay capabilities for dynamic transformations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ogimg.xyz&lt;/strong&gt; -- Dedicated API for OG image generation. Free tier with 50 images/month.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Puppeteer/Playwright&lt;/strong&gt; -- Screenshot approach. Flexible but slow.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The One-Sentence Summary
&lt;/h2&gt;

&lt;p&gt;Use &lt;strong&gt;1200 x 630 pixels&lt;/strong&gt;, &lt;strong&gt;PNG format&lt;/strong&gt;, &lt;strong&gt;under 1 MB&lt;/strong&gt;, with text inside a &lt;strong&gt;60px safe zone&lt;/strong&gt;, and always test with platform debugger tools before you ship.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Found this useful? Follow me for more practical web development guides.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>seo</category>
      <category>socialmedia</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to Add OG Images to Next.js in 5 Minutes</title>
      <dc:creator>Corbanware</dc:creator>
      <pubDate>Mon, 27 Apr 2026 01:01:38 +0000</pubDate>
      <link>https://dev.to/corbanware/how-to-add-og-images-to-nextjs-in-5-minutes-45nj</link>
      <guid>https://dev.to/corbanware/how-to-add-og-images-to-nextjs-in-5-minutes-45nj</guid>
      <description>&lt;h1&gt;
  
  
  How to Add OG Images to Next.js in 5 Minutes
&lt;/h1&gt;

&lt;p&gt;If you have ever shared a link on Twitter or Slack and watched it render as a sad, plain-text URL with no preview image, you already understand why OG images matter. Those 1200x630 pixel cards are the difference between a link that gets clicked and one that gets scrolled past. Studies show that links with rich preview images get 2-3x more engagement than bare text links.&lt;/p&gt;

&lt;p&gt;The good news: if you are building with Next.js and the App Router, adding OG images is remarkably straightforward. Let me walk you through three approaches, from the simplest to the most powerful.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Foundation: Understanding OG Meta Tags
&lt;/h2&gt;

&lt;p&gt;Before we touch any Next.js code, here is what the browser needs to see in your HTML &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;:&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://yoursite.com/og.png"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&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:width"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"1200"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&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:height"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"630"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&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:title"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Your Page Title"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&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:description"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"A brief description"&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 is it. Every social platform (Facebook, Twitter/X, LinkedIn, Slack, Discord, WhatsApp) reads these tags to build the preview card. The image URL &lt;strong&gt;must&lt;/strong&gt; be absolute -- relative paths will not work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach 1: Static Metadata (30 Seconds)
&lt;/h2&gt;

&lt;p&gt;For pages where the OG image never changes (your homepage, about page, pricing page), use the &lt;code&gt;metadata&lt;/code&gt; export in your layout or page file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/layout.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My Awesome App&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The best thing since sliced bread&lt;/span&gt;&lt;span class="dl"&gt;"&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;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My Awesome App&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The best thing since sliced bread&lt;/span&gt;&lt;span class="dl"&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="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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://yoursite.com/og-homepage.png&lt;/span&gt;&lt;span class="dl"&gt;"&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="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My Awesome App - homepage preview&lt;/span&gt;&lt;span class="dl"&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;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;twitter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;card&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;summary_large_image&lt;/span&gt;&lt;span class="dl"&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;p&gt;Next.js automatically renders the correct &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; tags in the HTML head. No manual string concatenation, no &lt;code&gt;dangerouslySetInnerHTML&lt;/code&gt;. It even handles the Twitter card fallback for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; Always include the &lt;code&gt;twitter.card: "summary_large_image"&lt;/code&gt; property. Without it, Twitter defaults to the small square card, which wastes most of your carefully designed image.&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach 2: Dynamic generateMetadata (2 Minutes)
&lt;/h2&gt;

&lt;p&gt;This is where things get interesting. For blogs, documentation, or any page with dynamic content, you want a unique OG image per page. The &lt;code&gt;generateMetadata&lt;/code&gt; async function lets you compute metadata at request time:&lt;br&gt;
&lt;/p&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;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getPostBySlug&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/lib/posts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&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="nx"&gt;Props&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Metadata&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&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="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;params&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;getPostBySlug&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;post&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&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="na"&gt;description&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="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;title&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="na"&gt;description&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="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;article&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;publishedTime&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;publishedAt&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="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="s2"&gt;`https://yoursite.com/api/og?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;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="s2"&gt;`&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;span class="na"&gt;twitter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;card&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;summary_large_image&lt;/span&gt;&lt;span class="dl"&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;p&gt;Notice the OG image URL points to an API route. That brings us to the next question: how do you actually &lt;strong&gt;generate&lt;/strong&gt; the image?&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach 3: Generating Images on the Fly
&lt;/h2&gt;

&lt;p&gt;You have two main options here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option A: @vercel/og (Self-Hosted)
&lt;/h3&gt;

&lt;p&gt;Vercel's @vercel/og library lets you write JSX that renders as an image. It uses Satori under the hood -- a layout engine that converts React components to SVG, then rasterizes to PNG.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/api/og/route.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ImageResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/og&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;runtime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;edge&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&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;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&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="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="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="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&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;title&lt;/span&gt; &lt;span class="o"&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My Blog&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ImageResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;100%&lt;/span&gt;&lt;span class="dl"&gt;'&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;100%&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;flexDirection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;column&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;justifyContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;alignItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;linear-gradient(135deg, #667eea 0%, #764ba2 100%)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fontWeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;textAlign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works well if you want full control over the design and are comfortable with Satori's CSS limitations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option B: External API
&lt;/h3&gt;

&lt;p&gt;If you would rather not maintain your own image generation endpoint, an external API simplifies things significantly. For example, with &lt;a href="https://ogimg.xyz?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=wf5" rel="noopener noreferrer"&gt;ogimg.xyz&lt;/a&gt;, your dynamic metadata becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ogUrl&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="s2"&gt;https://ogimg.xyz/api/og&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="nx"&gt;utm_source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;devto&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;utm_medium&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;utm_campaign&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;wf5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;ogUrl&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="s2"&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;ogUrl&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="s2"&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;ogUrl&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="s2"&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="s2"&gt;gradient&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No API route to maintain, no font files to bundle, no Satori quirks to debug. ogimg.xyz offers a free tier of 50 images/month, which is enough for most personal blogs and small projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Your OG Images
&lt;/h2&gt;

&lt;p&gt;After deploying, always verify your images actually work:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Facebook Sharing Debugger&lt;/strong&gt; -- Paste your URL and hit Scrape Again to force a fresh fetch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Twitter Card Validator&lt;/strong&gt; -- Preview exactly what Twitter will render.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LinkedIn Post Inspector&lt;/strong&gt; -- Check how LinkedIn will render your shared link.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open your browser DevTools&lt;/strong&gt; -- Search the HTML head for og:image and make sure the URL loads.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Quick Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;og:image URL is absolute (starts with https://)&lt;/li&gt;
&lt;li&gt;Image dimensions are 1200x630&lt;/li&gt;
&lt;li&gt;File size is under 1MB (5MB max for most platforms)&lt;/li&gt;
&lt;li&gt;twitter:card is set to summary_large_image&lt;/li&gt;
&lt;li&gt;Image renders correctly when you open the URL directly&lt;/li&gt;
&lt;li&gt;You have tested with at least one social platform debugger&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Adding proper OG images to a Next.js app is one of those 5-minute tasks that pays dividends every time someone shares your content. Whether you go with static metadata, dynamic generateMetadata, self-hosted @vercel/og, or an external service, the important thing is that you do it.&lt;/p&gt;

&lt;p&gt;Your links deserve better than plain text.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have questions about OG images in Next.js? Drop a comment below -- I read every one.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>GEO vs SEO: Why Optimizing for AI Search Is the Next Frontier</title>
      <dc:creator>Corbanware</dc:creator>
      <pubDate>Mon, 20 Apr 2026 01:00:50 +0000</pubDate>
      <link>https://dev.to/corbanware/geo-vs-seo-why-optimizing-for-ai-search-is-the-next-frontier-13f</link>
      <guid>https://dev.to/corbanware/geo-vs-seo-why-optimizing-for-ai-search-is-the-next-frontier-13f</guid>
      <description></description>
      <category>webdev</category>
      <category>ai</category>
      <category>saas</category>
      <category>programming</category>
    </item>
    <item>
      <title>Why Your Brand Is Invisible to ChatGPT (And How to Fix It)</title>
      <dc:creator>Corbanware</dc:creator>
      <pubDate>Mon, 13 Apr 2026 01:00:49 +0000</pubDate>
      <link>https://dev.to/corbanware/why-your-brand-is-invisible-to-chatgpt-and-how-to-fix-it-2721</link>
      <guid>https://dev.to/corbanware/why-your-brand-is-invisible-to-chatgpt-and-how-to-fix-it-2721</guid>
      <description></description>
      <category>webdev</category>
      <category>ai</category>
      <category>saas</category>
      <category>programming</category>
    </item>
    <item>
      <title>I Built an AI Search Visibility Auditor - Here Is What I Learned</title>
      <dc:creator>Corbanware</dc:creator>
      <pubDate>Mon, 06 Apr 2026 01:00:26 +0000</pubDate>
      <link>https://dev.to/corbanware/i-built-an-ai-search-visibility-auditor-here-is-what-i-learned-3j85</link>
      <guid>https://dev.to/corbanware/i-built-an-ai-search-visibility-auditor-here-is-what-i-learned-3j85</guid>
      <description></description>
      <category>webdev</category>
      <category>ai</category>
      <category>saas</category>
      <category>programming</category>
    </item>
    <item>
      <title>GEO vs SEO: Why Optimizing for AI Search Is the Next Frontier</title>
      <dc:creator>Corbanware</dc:creator>
      <pubDate>Mon, 30 Mar 2026 01:00:36 +0000</pubDate>
      <link>https://dev.to/corbanware/geo-vs-seo-why-optimizing-for-ai-search-is-the-next-frontier-254d</link>
      <guid>https://dev.to/corbanware/geo-vs-seo-why-optimizing-for-ai-search-is-the-next-frontier-254d</guid>
      <description></description>
      <category>webdev</category>
      <category>ai</category>
      <category>saas</category>
      <category>programming</category>
    </item>
    <item>
      <title>Why Your Brand Is Invisible to ChatGPT (And How to Fix It)</title>
      <dc:creator>Corbanware</dc:creator>
      <pubDate>Mon, 23 Mar 2026 01:00:31 +0000</pubDate>
      <link>https://dev.to/corbanware/why-your-brand-is-invisible-to-chatgpt-and-how-to-fix-it-3d25</link>
      <guid>https://dev.to/corbanware/why-your-brand-is-invisible-to-chatgpt-and-how-to-fix-it-3d25</guid>
      <description></description>
      <category>webdev</category>
      <category>ai</category>
      <category>saas</category>
      <category>programming</category>
    </item>
    <item>
      <title>I Built an AI Search Visibility Auditor - Here Is What I Learned</title>
      <dc:creator>Corbanware</dc:creator>
      <pubDate>Mon, 16 Mar 2026 01:01:11 +0000</pubDate>
      <link>https://dev.to/corbanware/i-built-an-ai-search-visibility-auditor-here-is-what-i-learned-1c58</link>
      <guid>https://dev.to/corbanware/i-built-an-ai-search-visibility-auditor-here-is-what-i-learned-1c58</guid>
      <description></description>
      <category>webdev</category>
      <category>ai</category>
      <category>saas</category>
      <category>programming</category>
    </item>
    <item>
      <title>Building a Multi-LLM Fallback System with Vercel AI SDK</title>
      <dc:creator>Corbanware</dc:creator>
      <pubDate>Mon, 09 Mar 2026 01:01:20 +0000</pubDate>
      <link>https://dev.to/corbanware/building-a-multi-llm-fallback-system-with-vercel-ai-sdk-1ijo</link>
      <guid>https://dev.to/corbanware/building-a-multi-llm-fallback-system-with-vercel-ai-sdk-1ijo</guid>
      <description>&lt;p&gt;A practical guide to implementing LLM provider fallback chains for production applications. Covers the architecture of Geonapse's 3-tier fallback system (Gemini Flash-Lite ? Groq ? DeepSeek), error handling patterns, cost optimization strategies, response quality validation, and monitoring. Includes code examples using Vercel AI SDK's unified interface and lessons learned from running multi-provider LLM systems in production.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>saas</category>
      <category>programming</category>
    </item>
    <item>
      <title>GEO vs SEO: Why Your Website Is Invisible to ChatGPT (And How to Fix It)</title>
      <dc:creator>Corbanware</dc:creator>
      <pubDate>Mon, 02 Mar 2026 01:01:17 +0000</pubDate>
      <link>https://dev.to/corbanware/geo-vs-seo-why-your-website-is-invisible-to-chatgpt-and-how-to-fix-it-57pi</link>
      <guid>https://dev.to/corbanware/geo-vs-seo-why-your-website-is-invisible-to-chatgpt-and-how-to-fix-it-57pi</guid>
      <description>&lt;p&gt;Generative Engine Optimization (GEO) is emerging as the next evolution of SEO. This article explores why traditional SEO techniques only partially work for AI search engines, what factors influence LLM brand recommendations, and a practical framework for auditing your AI search visibility. Covers knowledge graph presence, structured data importance, citation cascades, content structure optimization, and the growing importance of platform presence across Wikipedia, Crunchbase, G2, and other knowledge sources.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>saas</category>
      <category>programming</category>
    </item>
    <item>
      <title>I Built an AI Search Visibility Auditor Here's the Architecture</title>
      <dc:creator>Corbanware</dc:creator>
      <pubDate>Mon, 23 Feb 2026 01:01:07 +0000</pubDate>
      <link>https://dev.to/corbanware/i-built-an-ai-search-visibility-auditor-heres-the-architecture-7fb</link>
      <guid>https://dev.to/corbanware/i-built-an-ai-search-visibility-auditor-heres-the-architecture-7fb</guid>
      <description>&lt;p&gt;A technical deep-dive into building Geonapse, an AI search visibility auditor. Covers the 3-tier LLM fallback architecture (Gemini Flash-Lite ? Groq ? DeepSeek), the 40+ check audit engine, ranking detection via AI search APIs, and platform presence scanning across 8 knowledge platforms. Built with Next.js 16, Vercel AI SDK, Neon PostgreSQL, and Drizzle ORM. Includes lessons learned about LLM reliability, cost optimization, and building evaluation frameworks for AI-generated content.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>saas</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
