Dev.to WebDev πŸ›  Dev πŸ‘ 0 πŸ“– 3 min read

Hello DEV! I built a 100% client-side Open Graph & Social Thumbnail Previewer (Zero Login, Next.js)

Hey DEV community! πŸ‘‹ I'm excited to finally join DEV and share what I've been building, breaking, and shipping lately. Over the past few years working on web apps, technical blogs, and creator tools, I kept running int

Hey DEV community! πŸ‘‹

I'm excited to finally join DEV and share what I've been building, breaking, and shipping lately.

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

  1. You export a 1200Γ—630 Open Graph image (og:image) or a 1280Γ—720 YouTube / social graphic.
  2. You want to see how it actually renders inside X (summary_large_image), LinkedIn (1200Γ—627), Facebook, Discord's dark #2B2D31 embed, WhatsApp, or Instagram's 3:4 profile grid before deploying to production.
  3. 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.

So I decided to build a fast, 100% client-side, zero-login preview studio: SocialThumbs.com.

πŸ› οΈ What It Does (And Why Everything Runs in the Browser)

My main engineering rule for the project was simple: zero server uploads, zero accounts, and sub-second feedback.

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

// Zero-upload local preview lifecycle with automatic memory cleanup
const handleFileSelect = (file: File) => {
  if (previewUrl) {
    URL.revokeObjectURL(previewUrl);
  }
  const localBlobUrl = URL.createObjectURL(file);
  setPreviewUrl(localBlobUrl);
};

Here are the 8 interactive simulators currently live:

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

πŸ” 3 Weird Social Crawler Bugs I Discovered Along the Way

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

1. WhatsApp's Undocumented 300 KB Mobile Image Ceiling

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

2. Why X Crushes 1200Γ—630 Images Into a Tiny 120Γ—120 Square

Even though Twitterbot falls back to og:title, og:description, and og:image, it defaults the card layout to summary (a tiny 1:1 thumbnail) unless you explicitly include:

<meta name="twitter:card" content="summary_large_image" />

3. Headless Bots Don't Do TLS "AIA Chasing"

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

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 Broken Link Preview & Open Graph Troubleshooting Guide.

πŸ’¬ Let's Connect!

I'm planning to publish more deep-dives here on frontend performance, Next.js static exports, Open Graph automation, and indie tool building.

  • Have you ever been bitten by a cached or broken og:image right in the middle of a launch?
  • If you try out SocialThumbs.com, is there another platform embed (like Slack, Telegram, Bluesky, or Mastodon) you'd like me to add next?

Drop a comment below and say hiβ€”I'd love to check out what you're building too! πŸš€

πŸ“° Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes β€” full credit and traffic to the original publisher.