<?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: sara</title>
    <description>The latest articles on DEV Community by sara (@mllesara).</description>
    <link>https://dev.to/mllesara</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4238734%2F1ff4dbf0-1bbb-4680-b6cf-9b0e469911b2.png</url>
      <title>DEV Community: sara</title>
      <link>https://dev.to/mllesara</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mllesara"/>
    <language>en</language>
    <item>
      <title>Hello DEV! I built a 100% client-side Open Graph &amp; Social Thumbnail Previewer (Zero Login, Next.js)</title>
      <dc:creator>sara</dc:creator>
      <pubDate>Wed, 11 Nov 2026 09:37:23 +0000</pubDate>
      <link>https://dev.to/mllesara/hello-dev-i-built-a-100-client-side-open-graph-social-thumbnail-previewer-zero-login-nextjs-21p1</link>
      <guid>https://dev.to/mllesara/hello-dev-i-built-a-100-client-side-open-graph-social-thumbnail-previewer-zero-login-nextjs-21p1</guid>
      <description>&lt;p&gt;Hey DEV community! 👋&lt;/p&gt;

&lt;p&gt;I'm excited to finally join DEV and share what I've been building, breaking, and shipping lately.&lt;/p&gt;

&lt;p&gt;Over the past few years working on web apps, technical blogs, and creator tools, I kept running into the same annoying pre-launch ritual:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You export a &lt;code&gt;1200×630&lt;/code&gt; Open Graph image (&lt;code&gt;og:image&lt;/code&gt;) or a &lt;code&gt;1280×720&lt;/code&gt; YouTube / social graphic.&lt;/li&gt;
&lt;li&gt;You want to see how it actually renders inside &lt;strong&gt;X (&lt;code&gt;summary_large_image&lt;/code&gt;), LinkedIn (&lt;code&gt;1200×627&lt;/code&gt;), Facebook, Discord's dark &lt;code&gt;#2B2D31&lt;/code&gt; embed, WhatsApp, or Instagram's &lt;code&gt;3:4&lt;/code&gt; profile grid&lt;/strong&gt; &lt;em&gt;before&lt;/em&gt; deploying to production.&lt;/li&gt;
&lt;li&gt;Every existing preview tool either requires your URL to already be live on a public HTTPS domain (like Meta Sharing Debugger or LinkedIn Post Inspector) or forces you to create an account, hit a paywall, and upload your local files to a remote server.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So I decided to build a fast, 100% client-side, zero-login preview studio: &lt;strong&gt;&lt;a href="https://socialthumbs.com/" rel="noopener noreferrer"&gt;SocialThumbs.com&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ What It Does (And Why Everything Runs in the Browser)
&lt;/h2&gt;

&lt;p&gt;My main engineering rule for the project was simple: &lt;strong&gt;zero server uploads, zero accounts, and sub-second feedback&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When you drag and drop a local image file (&lt;code&gt;PNG&lt;/code&gt;, &lt;code&gt;JPG&lt;/code&gt;, &lt;code&gt;WebP&lt;/code&gt;, or &lt;code&gt;AVIF&lt;/code&gt;) into any of the 8 platform simulators, the file never leaves your machine. Everything is rendered in memory via the browser's &lt;code&gt;File&lt;/code&gt; and &lt;code&gt;URL.createObjectURL()&lt;/code&gt; APIs inside pixel-accurate DOM replicas of each platform's feed container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Zero-upload local preview lifecycle with automatic memory cleanup&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;handleFileSelect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;File&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;previewUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="no"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;revokeObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;previewUrl&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="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;localBlobUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;setPreviewUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;localBlobUrl&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;p&gt;Here are the 8 interactive simulators currently live:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://socialthumbs.com/open-graph" rel="noopener noreferrer"&gt;Open Graph (&lt;code&gt;og:image&lt;/code&gt;) 5-Platform Previewer + HTML &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; Generator&lt;/a&gt;&lt;/strong&gt;: Upload a local &lt;code&gt;1200×630&lt;/code&gt; banner, type your &lt;code&gt;og:title&lt;/code&gt; and &lt;code&gt;og:description&lt;/code&gt;, and preview X (&lt;code&gt;1.91:1&lt;/code&gt; with domain pill overlay), LinkedIn (&lt;code&gt;1200×627&lt;/code&gt;), Facebook (&lt;code&gt;1200×630&lt;/code&gt;), Discord (&lt;code&gt;#2B2D31&lt;/code&gt; dark card with customizable &lt;code&gt;theme-color&lt;/code&gt; border), and WhatsApp—plus copy the production HTML &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; snippet in one click.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://socialthumbs.com/youtube" rel="noopener noreferrer"&gt;YouTube Thumbnail Preview &amp;amp; A/B Tester&lt;/a&gt;&lt;/strong&gt;: Compare Variant A vs. Variant B side-by-side across Desktop Home, Search, and the tiny &lt;strong&gt;&lt;code&gt;168×94 px&lt;/code&gt; Suggested Video sidebar&lt;/strong&gt;, toggle Dark Mode (&lt;code&gt;#0F0F0F&lt;/code&gt;), and check the &lt;code&gt;12:45&lt;/code&gt; bottom-right timestamp overlay (&lt;code&gt;140×60 px&lt;/code&gt; dead zone).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://socialthumbs.com/instagram" rel="noopener noreferrer"&gt;Instagram &lt;code&gt;4:5&lt;/code&gt; Feed &amp;amp; &lt;code&gt;3:4&lt;/code&gt; Profile Grid Simulator&lt;/a&gt;&lt;/strong&gt;: Preview how &lt;code&gt;1080×1350&lt;/code&gt; (&lt;code&gt;4:5&lt;/code&gt;) carousels crop horizontally to &lt;code&gt;1012.5×1350&lt;/code&gt; (&lt;code&gt;-33.75 px&lt;/code&gt; on left and right) inside Instagram's &lt;code&gt;3:4&lt;/code&gt; vertical profile grid without logging into Instagram.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://socialthumbs.com/tiktok" rel="noopener noreferrer"&gt;TikTok, Reels &amp;amp; Shorts &lt;code&gt;9:16&lt;/code&gt; Safe-Zone Checker&lt;/a&gt;&lt;/strong&gt;: Toggle the exact &lt;code&gt;1080×1920&lt;/code&gt; UI danger zones (top &lt;code&gt;150 px&lt;/code&gt; header, right &lt;code&gt;120 px&lt;/code&gt; action icon bar, and bottom &lt;code&gt;320 px&lt;/code&gt; caption/audio dock).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://socialthumbs.com/twitter" rel="noopener noreferrer"&gt;X / Twitter&lt;/a&gt;, &lt;a href="https://socialthumbs.com/linkedin" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, &lt;a href="https://socialthumbs.com/facebook" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt; &amp;amp; &lt;a href="https://socialthumbs.com/pinterest" rel="noopener noreferrer"&gt;Pinterest&lt;/a&gt;&lt;/strong&gt;: Compare Native &lt;code&gt;16:9&lt;/code&gt; / &lt;code&gt;4:5&lt;/code&gt; image posts against &lt;code&gt;1.91:1&lt;/code&gt; Link Cards and test LinkedIn's &lt;code&gt;210-character&lt;/code&gt; &lt;code&gt;"…more"&lt;/code&gt; fold cutoff in real time.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔍 3 Weird Social Crawler Bugs I Discovered Along the Way
&lt;/h2&gt;

&lt;p&gt;Building the previewers and writing the engineering documentation led me down a deep rabbit hole of how social crawlers (&lt;code&gt;Twitterbot/1.0&lt;/code&gt;, &lt;code&gt;LinkedInBot/1.0&lt;/code&gt;, &lt;code&gt;facebookexternalhit/1.1&lt;/code&gt;, &lt;code&gt;Discordbot/2.0&lt;/code&gt;, and &lt;code&gt;WhatsApp/2.x&lt;/code&gt;) actually parse pages in production:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. WhatsApp's Undocumented &lt;code&gt;300 KB&lt;/code&gt; Mobile Image Ceiling
&lt;/h3&gt;

&lt;p&gt;While Facebook (&lt;code&gt;8 MB&lt;/code&gt;) and X (&lt;code&gt;5 MB&lt;/code&gt;) happily download large PNGs, WhatsApp's mobile link preview fetcher (&lt;code&gt;WhatsApp/2.x&lt;/code&gt;) silently aborts downloading your &lt;code&gt;og:image&lt;/code&gt; on cellular connections if the payload exceeds &lt;strong&gt;&lt;code&gt;~300 KB&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;600 KB&lt;/code&gt; max on Wi-Fi). If you use &lt;code&gt;@vercel/og&lt;/code&gt; (&lt;code&gt;ImageResponse&lt;/code&gt;), exporting a progressive JPEG or WebP under &lt;code&gt;250 KB&lt;/code&gt; is the difference between a rich preview and a blank gray box.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Why X Crushes &lt;code&gt;1200×630&lt;/code&gt; Images Into a Tiny &lt;code&gt;120×120&lt;/code&gt; Square
&lt;/h3&gt;

&lt;p&gt;Even though &lt;code&gt;Twitterbot&lt;/code&gt; falls back to &lt;code&gt;og:title&lt;/code&gt;, &lt;code&gt;og:description&lt;/code&gt;, and &lt;code&gt;og:image&lt;/code&gt;, it defaults the card layout to &lt;code&gt;summary&lt;/code&gt; (a tiny &lt;code&gt;1:1&lt;/code&gt; thumbnail) unless you explicitly include:&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;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"twitter:card"&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"summary_large_image"&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;
  
  
  3. Headless Bots Don't Do TLS "AIA Chasing"
&lt;/h3&gt;

&lt;p&gt;Desktop browsers (Chrome, Safari, Firefox) automatically fetch missing intermediate SSL certificates via Authority Information Access (&lt;code&gt;AIA&lt;/code&gt;). Social scrapers (&lt;code&gt;LinkedInBot&lt;/code&gt;, &lt;code&gt;Twitterbot&lt;/code&gt;, &lt;code&gt;Discordbot&lt;/code&gt;) do &lt;strong&gt;not&lt;/strong&gt;—if your server only serves &lt;code&gt;cert.pem&lt;/code&gt; instead of &lt;code&gt;fullchain.pem&lt;/code&gt;, the bot drops the TLS handshake and shows zero link preview even though your site shows a green padlock in Chrome!&lt;/p&gt;

&lt;p&gt;If you ever need a quick checklist when debugging a stubborn social card, I documented all the crawler rules and cache-busting tricks in this &lt;a href="https://socialthumbs.com/blog/thumbnail-link-preview-not-showing-fix" rel="noopener noreferrer"&gt;Broken Link Preview &amp;amp; Open Graph Troubleshooting Guide&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  💬 Let's Connect!
&lt;/h2&gt;

&lt;p&gt;I'm planning to publish more deep-dives here on &lt;strong&gt;frontend performance, Next.js static exports, Open Graph automation, and indie tool building&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have you ever been bitten by a cached or broken &lt;code&gt;og:image&lt;/code&gt; right in the middle of a launch?&lt;/li&gt;
&lt;li&gt;If you try out &lt;strong&gt;&lt;a href="https://socialthumbs.com/open-graph" rel="noopener noreferrer"&gt;SocialThumbs.com&lt;/a&gt;&lt;/strong&gt;, is there another platform embed (like Slack, Telegram, Bluesky, or Mastodon) you'd like me to add next?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Drop a comment below and say hi—I'd love to check out what you're building too! 🚀&lt;/p&gt;

</description>
      <category>welcome</category>
      <category>webdev</category>
      <category>showdev</category>
      <category>nextjs</category>
    </item>
  </channel>
</rss>
