<?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: Narender singh</title>
    <description>The latest articles on DEV Community by Narender singh (@narender_singh_6c6e271c67).</description>
    <link>https://dev.to/narender_singh_6c6e271c67</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%2F3841987%2Fb7fcea0b-f246-4a4a-a244-8977e9aa202d.png</url>
      <title>DEV Community: Narender singh</title>
      <link>https://dev.to/narender_singh_6c6e271c67</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/narender_singh_6c6e271c67"/>
    <language>en</language>
    <item>
      <title>Building a Link Shortener? Here's What I Learned About Click Analytics</title>
      <dc:creator>Narender singh</dc:creator>
      <pubDate>Sun, 29 Mar 2026 13:58:52 +0000</pubDate>
      <link>https://dev.to/narender_singh_6c6e271c67/building-a-link-shortener-heres-what-i-learned-about-click-analytics-go</link>
      <guid>https://dev.to/narender_singh_6c6e271c67/building-a-link-shortener-heres-what-i-learned-about-click-analytics-go</guid>
      <description>&lt;p&gt;I built a link shortener called &lt;a href="https://briefly-iota.vercel.app" rel="noopener noreferrer"&gt;Briefly&lt;/a&gt; a few months ago. It started as a weekend project. "How hard can it be?" I thought. Redirect one URL to another. Done.&lt;/p&gt;

&lt;p&gt;The redirect part was easy. The analytics part taught me things I didn't expect.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Redirect Is the Easy Part
&lt;/h2&gt;

&lt;p&gt;Seriously. A link shortener's core logic is maybe 20 lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&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="s1"&gt;/:code&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;link&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&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;code&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;link&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Track the click (async, don't block redirect)&lt;/span&gt;
  &lt;span class="nf"&gt;trackClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;301&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;originalUrl&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;But the interesting stuff happens in &lt;code&gt;trackClick&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Analytics Actually Matter
&lt;/h2&gt;

&lt;p&gt;When I first added analytics, I tracked everything. Timestamp. IP. User agent. Referrer. Screen size. Language. Every header I could grab.&lt;/p&gt;

&lt;p&gt;Most of it was useless noise.&lt;/p&gt;

&lt;p&gt;Here's what actually matters when you're sharing links:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Click Count Over Time
&lt;/h3&gt;

&lt;p&gt;Not just total clicks. Clicks per hour. Per day. This tells you &lt;em&gt;when&lt;/em&gt; your audience is active. I found that links I shared on Twitter got 80% of their clicks in the first 4 hours. Reddit links had a slower burn over 2-3 days.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Referrer Source
&lt;/h3&gt;

&lt;p&gt;Where did the click come from? This is gold. You'll quickly learn which platforms drive real traffic and which are vanity metrics.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;parseReferrer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;referrerUrl&lt;/span&gt;&lt;span class="p"&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;referrerUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;direct&lt;/span&gt;&lt;span class="dl"&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;hostname&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;referrerUrl&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;hostname&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;sources&lt;/span&gt; &lt;span class="o"&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;t.co&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;twitter&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;twitter.com&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;twitter&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;x.com&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;twitter&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;reddit.com&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;reddit&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;old.reddit.com&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;reddit&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;linkedin.com&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;linkedin&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;facebook.com&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;facebook&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;hostname&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;
  
  
  3. Geographic Data (Country Level)
&lt;/h3&gt;

&lt;p&gt;You don't need city-level precision. Country is enough. It tells you if your content is reaching the right audience. I was surprised to find that 40% of my traffic came from India and Brazil — markets I wasn't even thinking about.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Device Type
&lt;/h3&gt;

&lt;p&gt;Mobile vs desktop. That's it. Don't overthink this. But it matters because if 70% of your clicks are mobile, your landing page better work on phones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mistakes I Made
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Mistake 1: Blocking the redirect for analytics.&lt;/strong&gt; Don't do this. Track asynchronously. Every millisecond of redirect delay costs you clicks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 2: Storing raw IPs.&lt;/strong&gt; Privacy laws exist. Hash them or use them only for unique visitor counting, then discard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 3: Not deduplicating.&lt;/strong&gt; One person refreshing counts as one click, not ten. Use a combination of hashed IP + user agent + time window.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isUniqueClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;linkId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;visitorHash&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="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 24 hours&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recent&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findRecentClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;linkId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;visitorHash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;recent&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;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;If I started over, I'd build the analytics dashboard before the shortener. Sounds backwards, but the dashboard design tells you exactly what data to collect. No more, no less.&lt;/p&gt;

&lt;p&gt;I'd also add UTM parameter support from day one. Being able to append UTM tags automatically when creating short links saves a ton of manual work.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://briefly-iota.vercel.app" rel="noopener noreferrer"&gt;Briefly&lt;/a&gt; is free to use. Create short links, track clicks, see where your traffic comes from. No signup wall for basic features.&lt;/p&gt;

&lt;p&gt;The code taught me more about web analytics than any course could. Sometimes the best way to learn is to build the thing.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Generate Open Graph Images Dynamically (No Puppeteer)</title>
      <dc:creator>Narender singh</dc:creator>
      <pubDate>Sun, 29 Mar 2026 13:58:48 +0000</pubDate>
      <link>https://dev.to/narender_singh_6c6e271c67/how-to-generate-open-graph-images-dynamically-no-puppeteer-30kf</link>
      <guid>https://dev.to/narender_singh_6c6e271c67/how-to-generate-open-graph-images-dynamically-no-puppeteer-30kf</guid>
      <description>&lt;p&gt;You know what's annoying? Spinning up a headless browser just to generate a social preview image.&lt;/p&gt;

&lt;p&gt;Puppeteer works. Sure. But it's slow, memory-hungry, and a nightmare to deploy on serverless. I spent way too long fighting Chrome binaries on Vercel before I gave up and looked for something better.&lt;/p&gt;

&lt;p&gt;Turns out there's a simpler way.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem With Puppeteer-Based OG Images
&lt;/h2&gt;

&lt;p&gt;Here's the typical flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Spin up a headless Chrome instance&lt;/li&gt;
&lt;li&gt;Render an HTML page with your dynamic content&lt;/li&gt;
&lt;li&gt;Screenshot it&lt;/li&gt;
&lt;li&gt;Serve that screenshot as your OG image&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It works on your local machine. Then you deploy it and everything breaks. Chrome needs specific system libraries. Cold starts take 5-10 seconds. Memory usage spikes. And if you're on a free tier anywhere, good luck.&lt;/p&gt;

&lt;p&gt;I've burned entire weekends on this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter OGPix
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://ogpix-pi.vercel.app" rel="noopener noreferrer"&gt;OGPix&lt;/a&gt; takes a completely different approach. Instead of running a browser, it generates images through an API. You send parameters, you get back an image URL. That's it.&lt;/p&gt;

&lt;p&gt;No Chrome. No Puppeteer. No deployment headaches.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works — Step by Step
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Pick a Template or Build Your Own
&lt;/h3&gt;

&lt;p&gt;OGPix comes with pre-built templates for blog posts, product pages, and documentation. But you can also customize everything.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Generate via URL Parameters
&lt;/h3&gt;

&lt;p&gt;The simplest approach — just construct a URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://ogpix-pi.vercel.app/api/og?title=My+Blog+Post&amp;amp;description=A+quick+tutorial&amp;amp;theme=dark
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Drop that into your meta tags:&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://ogpix-pi.vercel.app/api/og?title=My+Blog+Post&amp;amp;description=A+quick+tutorial&amp;amp;theme=dark"&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;name=&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="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. Use It in Next.js
&lt;/h3&gt;

&lt;p&gt;If you're on Next.js, add it to your layout or page head:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&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="o"&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;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="err"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;`https://ogpix-pi.vercel.app/api/og?title=&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;{encodeURIComponent(post.title)}&lt;/span&gt;&lt;span class="se"&gt;\`&lt;/span&gt;&lt;span class="s2"&gt;,
        width: 1200,
        height: 630,
      },
    ],
  },
};
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Dynamic Routes? No Problem
&lt;/h3&gt;

&lt;p&gt;For blog posts or any dynamic content, just swap in the variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getOGImage&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;author&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;params&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;URLSearchParams&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;author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gradient&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="k"&gt;return&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;`https://ogpix-pi.vercel.app/api/og?&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;{params.toString()}&lt;/span&gt;&lt;span class="se"&gt;\`&lt;/span&gt;&lt;span class="s2"&gt;;
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why This Beats Puppeteer
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Speed&lt;/strong&gt;: Images generate in milliseconds, not seconds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No dependencies&lt;/strong&gt;: Zero system libraries to install&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serverless-friendly&lt;/strong&gt;: Works everywhere, including edge functions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching&lt;/strong&gt;: URLs are deterministic, so CDN caching just works&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free&lt;/strong&gt;: No paid tiers for basic usage&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Results
&lt;/h2&gt;

&lt;p&gt;I switched three of my projects from Puppeteer to OGPix. Deploy times dropped. No more random failures in CI. And the images actually look better because I'm not fighting CSS rendering inconsistencies across headless browsers.&lt;/p&gt;

&lt;p&gt;If you're still wrestling with Puppeteer for OG images, stop. There's a better way now.&lt;/p&gt;

&lt;p&gt;Check it out at &lt;a href="https://ogpix-pi.vercel.app" rel="noopener noreferrer"&gt;ogpix-pi.vercel.app&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>seo</category>
      <category>nextjs</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The Lazy Developer's Guide to Perfect Social Media Previews</title>
      <dc:creator>Narender singh</dc:creator>
      <pubDate>Thu, 26 Mar 2026 17:00:40 +0000</pubDate>
      <link>https://dev.to/narender_singh_6c6e271c67/the-lazy-developers-guide-to-perfect-social-media-previews-39im</link>
      <guid>https://dev.to/narender_singh_6c6e271c67/the-lazy-developers-guide-to-perfect-social-media-previews-39im</guid>
      <description>&lt;p&gt;You finally ship your project. You share the link on Twitter. And the preview looks like... nothing. A blank card. Or worse, some random text fragment from your footer.&lt;/p&gt;

&lt;p&gt;I've been there. Multiple times. And it's always the same problem: Open Graph tags.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are OG Tags?
&lt;/h2&gt;

&lt;p&gt;Open Graph tags are meta tags in your HTML &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; that tell social platforms what to show when someone shares your link. Facebook invented the protocol, but Twitter, LinkedIn, Slack, Discord, and basically every platform uses them now.&lt;/p&gt;

&lt;p&gt;Here are the important ones:&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;head&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;"My Awesome Project"&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 short description of what this does"&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"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"https://example.com/preview.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:url"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"https://example.com"&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:type"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"website"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Twitter-specific (yes, they still need their own) --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&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="s"&gt;"summary_large_image"&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;name=&lt;/span&gt;&lt;span class="s"&gt;"twitter:title"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"My Awesome Project"&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;name=&lt;/span&gt;&lt;span class="s"&gt;"twitter:description"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"A short description"&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;name=&lt;/span&gt;&lt;span class="s"&gt;"twitter:image"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"https://example.com/preview.png"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Six to eight meta tags and your links look professional everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 5 Mistakes Everyone Makes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Forgetting the Image
&lt;/h3&gt;

&lt;p&gt;No &lt;code&gt;og:image&lt;/code&gt;? Most platforms will either show nothing or try to grab a random image from your page. Sometimes it's your logo. Sometimes it's an icon. Sometimes it's a tracking pixel. Fun.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Wrong Image Dimensions
&lt;/h3&gt;

&lt;p&gt;The magic number is &lt;strong&gt;1200x630 pixels&lt;/strong&gt;. That's the size that looks good everywhere. Go smaller and platforms will crop it weird or add gray bars. Go too big and some platforms reject it entirely.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Using a Relative Image URL
&lt;/h3&gt;

&lt;p&gt;This won't work:&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="c"&gt;&amp;lt;!-- BAD --&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"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"/images/preview.png"&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;Social media crawlers don't know your domain. Use the full URL:&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="c"&gt;&amp;lt;!-- GOOD --&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"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"https://yoursite.com/images/preview.png"&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;
  
  
  4. Not Setting twitter:card
&lt;/h3&gt;

&lt;p&gt;Without &lt;code&gt;twitter:card&lt;/code&gt;, Twitter defaults to a tiny thumbnail. Add &lt;code&gt;summary_large_image&lt;/code&gt; to get the full-width preview card. One line of HTML makes a massive difference.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Caching Issues
&lt;/h3&gt;

&lt;p&gt;You fixed your tags but the preview still looks wrong? Social platforms cache aggressively. Twitter caches for about 7 days. Facebook has its own timeline.&lt;/p&gt;

&lt;p&gt;You can force a refresh:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Twitter&lt;/strong&gt;: Use the &lt;a href="https://cards-dev.twitter.com/validator" rel="noopener noreferrer"&gt;Card Validator&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Facebook&lt;/strong&gt;: Use the &lt;a href="https://developers.facebook.com/tools/debug/" rel="noopener noreferrer"&gt;Sharing Debugger&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LinkedIn&lt;/strong&gt;: Append a query parameter like &lt;code&gt;?v=2&lt;/code&gt; to bust the cache&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Dynamic Image Problem
&lt;/h2&gt;

&lt;p&gt;Static OG images work fine for your homepage. But what about blog posts? Product pages? User profiles? You can't manually create an image for every page.&lt;/p&gt;

&lt;p&gt;This is where &lt;a href="https://ogpix-pi.vercel.app" rel="noopener noreferrer"&gt;OGPix&lt;/a&gt; comes in. It generates OG images dynamically through URL parameters.&lt;/p&gt;

&lt;p&gt;Instead of creating images in Figma, you construct a URL:&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://ogpix-pi.vercel.app/api/og?title=My+Blog+Post&amp;amp;theme=dark"&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;Change the title parameter, get a different image. No design tools. No image hosting. No build step.&lt;/p&gt;

&lt;p&gt;For a Next.js blog, it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&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="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;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;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="err"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;`https://ogpix-pi.vercel.app/api/og?title=&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;{encodeURIComponent(post.title)}&lt;/span&gt;&lt;span class="se"&gt;\`&lt;/span&gt;&lt;span class="s2"&gt;,
        width: 1200,
        height: 630,
      }],
    },
  };
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every blog post gets a unique, properly-sized preview image. Automatically.&lt;/p&gt;

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

&lt;p&gt;Before you share any project link:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;code&gt;og:title&lt;/code&gt; is set and under 60 characters&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;og:description&lt;/code&gt; is set and under 155 characters&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;og:image&lt;/code&gt; uses an absolute URL and is 1200x630&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;twitter:card&lt;/code&gt; is set to &lt;code&gt;summary_large_image&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Test with Twitter Card Validator and Facebook Debugger&lt;/li&gt;
&lt;li&gt;[ ] Preview looks good on mobile (text isn't tiny)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Stop Shipping Blank Previews
&lt;/h2&gt;

&lt;p&gt;Good OG tags take 5 minutes to add. Bad previews cost you clicks every single day. Your project deserves better than a blank card on Twitter.&lt;/p&gt;

&lt;p&gt;Set up your meta tags. Use &lt;a href="https://ogpix-pi.vercel.app" rel="noopener noreferrer"&gt;OGPix&lt;/a&gt; for dynamic images. Test before you share. That's it.&lt;/p&gt;

&lt;p&gt;It's the laziest high-impact improvement you can make.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>seo</category>
      <category>social</category>
      <category>beginners</category>
    </item>
    <item>
      <title>I Made 21 Free Browser Tools So You Don't Have to Install Anything</title>
      <dc:creator>Narender singh</dc:creator>
      <pubDate>Thu, 26 Mar 2026 14:06:20 +0000</pubDate>
      <link>https://dev.to/narender_singh_6c6e271c67/i-made-21-free-browser-tools-so-you-dont-have-to-install-anything-220a</link>
      <guid>https://dev.to/narender_singh_6c6e271c67/i-made-21-free-browser-tools-so-you-dont-have-to-install-anything-220a</guid>
      <description>&lt;p&gt;Every time I needed a quick color converter or JSON formatter, I'd either:&lt;/p&gt;

&lt;p&gt;A) Google it and land on an ad-riddled site that loads 47 trackers&lt;br&gt;
B) Install yet another VS Code extension&lt;br&gt;
C) Write a one-off script and forget where I saved it&lt;/p&gt;

&lt;p&gt;So I built all the tools I keep reaching for and put them in one place. No installs. No accounts. No ads. Just browser-based utilities at &lt;a href="https://ogpix-pi.vercel.app/tools" rel="noopener noreferrer"&gt;ogpix-pi.vercel.app/tools&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's everything and why each one exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  Image &amp;amp; Visual Tools
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. OG Image Generator&lt;/strong&gt; — Create Open Graph preview images with custom text, colors, and layouts. The tool that started this whole project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Favicon Generator&lt;/strong&gt; — Upload an image, get favicons in all the sizes browsers actually need. ICO, PNG, Apple Touch. The whole set.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Image Resizer&lt;/strong&gt; — Resize and crop images to specific dimensions. Preset sizes for social platforms. Everything runs client-side.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Color Palette Generator&lt;/strong&gt; — Pick a base color, get complementary, analogous, and triadic palettes. Copy hex codes with one click.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Screenshot Mockup&lt;/strong&gt; — Drop in a screenshot, get it wrapped in a browser frame or phone mockup. Great for portfolio sites and READMEs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Text &amp;amp; Content Tools
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;6. Markdown Previewer&lt;/strong&gt; — Write markdown on the left, see rendered HTML on the right. Live preview. Supports GFM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Lorem Ipsum Generator&lt;/strong&gt; — But not the boring kind. Generate placeholder text by paragraphs, sentences, or words. Pick different styles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. Word Counter&lt;/strong&gt; — Paste text, get word count, character count, reading time, and sentence count. I use this for every blog post.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;9. Case Converter&lt;/strong&gt; — camelCase, snake_case, UPPER_CASE, Title Case, kebab-case. Paste and convert.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;10. Text Diff Tool&lt;/strong&gt; — Paste two blocks of text, see what changed. Highlighted additions and deletions. Like a mini Git diff.&lt;/p&gt;

&lt;h2&gt;
  
  
  Developer Tools
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;11. JSON Formatter&lt;/strong&gt; — Paste ugly JSON, get pretty JSON. Validates too. Catches missing commas before your parser does.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;12. Base64 Encoder/Decoder&lt;/strong&gt; — Encode and decode Base64 strings. Also handles file-to-Base64 for data URIs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;13. URL Encoder/Decoder&lt;/strong&gt; — Because I can never remember what needs percent-encoding and what doesn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;14. Hash Generator&lt;/strong&gt; — MD5, SHA-1, SHA-256. Paste text or upload a file. Useful for verifying downloads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;15. Regex Tester&lt;/strong&gt; — Write a regex pattern, test it against sample text. Shows matches, groups, and explains the pattern. Saves me from regex101 tab-hopping.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;16. JWT Decoder&lt;/strong&gt; — Paste a JWT, see the header and payload decoded. No libraries needed.&lt;/p&gt;

&lt;p&gt;\&lt;/p&gt;

&lt;h2&gt;
  
  
  CSS &amp;amp; Design Tools
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;17. CSS Gradient Generator&lt;/strong&gt; — Pick colors and direction, get the CSS. Linear and radial gradients with live preview.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;18. Box Shadow Generator&lt;/strong&gt; — Tweak offset, blur, spread, and color. Copy the CSS. Visual feedback while you adjust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;19. Border Radius Previewer&lt;/strong&gt; — Drag corners independently. Get the border-radius shorthand. Helpful for those asymmetric shapes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conversion Tools
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;20. Timestamp Converter&lt;/strong&gt; — Unix timestamp to human date and back. Shows UTC, local time, and ISO 8601. I check this at least once a week.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;21. Unit Converter (CSS)&lt;/strong&gt; — px to rem, em to px, viewport units. Set your base font size and convert between units.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Browser-Based?
&lt;/h2&gt;

&lt;p&gt;Privacy. Your data stays in your browser. I don't want your JSON payloads or your JWTs. There's no backend collecting anything because there's no backend at all for these tools.&lt;/p&gt;

&lt;p&gt;And speed. No server round-trips means instant results. Type and see output immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try Them
&lt;/h2&gt;

&lt;p&gt;All 21 tools are at &lt;a href="https://ogpix-pi.vercel.app/tools" rel="noopener noreferrer"&gt;ogpix-pi.vercel.app/tools&lt;/a&gt;. Bookmark it. I add new tools whenever I find myself reaching for something that should just exist in a browser tab.&lt;/p&gt;

&lt;p&gt;If there's a tool you wish existed, open an issue or DM me. I'll probably build it next weekend.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>javascript</category>
      <category>tools</category>
    </item>
    <item>
      <title>Free Uptime Monitoring in 2026 — Stop Paying for Basic Alerts</title>
      <dc:creator>Narender singh</dc:creator>
      <pubDate>Thu, 26 Mar 2026 14:05:33 +0000</pubDate>
      <link>https://dev.to/narender_singh_6c6e271c67/free-uptime-monitoring-in-2026-stop-paying-for-basic-alerts-bgo</link>
      <guid>https://dev.to/narender_singh_6c6e271c67/free-uptime-monitoring-in-2026-stop-paying-for-basic-alerts-bgo</guid>
      <description>&lt;p&gt;$20/month for uptime monitoring. That's what I was paying. To check if a website returns a 200 status code. Every 5 minutes.&lt;/p&gt;

&lt;p&gt;Let that sink in. Twenty dollars a month to run \&lt;/p&gt;

</description>
      <category>devops</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Building a Link Shortener? Here's What I Learned About Click Analytics</title>
      <dc:creator>Narender singh</dc:creator>
      <pubDate>Thu, 26 Mar 2026 13:45:33 +0000</pubDate>
      <link>https://dev.to/narender_singh_6c6e271c67/building-a-link-shortener-heres-what-i-learned-about-click-analytics-253i</link>
      <guid>https://dev.to/narender_singh_6c6e271c67/building-a-link-shortener-heres-what-i-learned-about-click-analytics-253i</guid>
      <description>&lt;p&gt;I built a link shortener called &lt;a href="https://briefly-iota.vercel.app" rel="noopener noreferrer"&gt;Briefly&lt;/a&gt; a few months ago. It started as a weekend project. "How hard can it be?" I thought. Redirect one URL to another. Done.&lt;/p&gt;

&lt;p&gt;The redirect part was easy. The analytics part taught me things I didn't expect.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Redirect Is the Easy Part
&lt;/h2&gt;

&lt;p&gt;Seriously. A link shortener's core logic is maybe 20 lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&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="s1"&gt;/:code&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;link&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&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;code&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;link&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Track the click (async, don't block redirect)&lt;/span&gt;
  &lt;span class="nf"&gt;trackClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;301&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;originalUrl&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;But the interesting stuff happens in &lt;code&gt;trackClick&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Analytics Actually Matter
&lt;/h2&gt;

&lt;p&gt;When I first added analytics, I tracked everything. Timestamp. IP. User agent. Referrer. Screen size. Language. Every header I could grab.&lt;/p&gt;

&lt;p&gt;Most of it was useless noise.&lt;/p&gt;

&lt;p&gt;Here's what actually matters when you're sharing links:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Click Count Over Time
&lt;/h3&gt;

&lt;p&gt;Not just total clicks. Clicks per hour. Per day. This tells you &lt;em&gt;when&lt;/em&gt; your audience is active. I found that links I shared on Twitter got 80% of their clicks in the first 4 hours. Reddit links had a slower burn over 2-3 days.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Referrer Source
&lt;/h3&gt;

&lt;p&gt;Where did the click come from? This is gold. You'll quickly learn which platforms drive real traffic and which are vanity metrics.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;parseReferrer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;referrerUrl&lt;/span&gt;&lt;span class="p"&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;referrerUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;direct&lt;/span&gt;&lt;span class="dl"&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;hostname&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;referrerUrl&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;hostname&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;sources&lt;/span&gt; &lt;span class="o"&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;t.co&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;twitter&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;twitter.com&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;twitter&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;x.com&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;twitter&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;reddit.com&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;reddit&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;old.reddit.com&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;reddit&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;linkedin.com&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;linkedin&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;facebook.com&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;facebook&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;hostname&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;
  
  
  3. Geographic Data (Country Level)
&lt;/h3&gt;

&lt;p&gt;You don't need city-level precision. Country is enough. It tells you if your content is reaching the right audience. I was surprised to find that 40% of my traffic came from India and Brazil — markets I wasn't even thinking about.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Device Type
&lt;/h3&gt;

&lt;p&gt;Mobile vs desktop. That's it. Don't overthink this. But it matters because if 70% of your clicks are mobile, your landing page better work on phones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mistakes I Made
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Mistake 1: Blocking the redirect for analytics.&lt;/strong&gt; Don't do this. Track asynchronously. Every millisecond of redirect delay costs you clicks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 2: Storing raw IPs.&lt;/strong&gt; Privacy laws exist. Hash them or use them only for unique visitor counting, then discard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 3: Not deduplicating.&lt;/strong&gt; One person refreshing counts as one click, not ten. Use a combination of hashed IP + user agent + time window.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isUniqueClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;linkId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;visitorHash&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="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 24 hours&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recent&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findRecentClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;linkId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;visitorHash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;recent&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;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;If I started over, I'd build the analytics dashboard before the shortener. Sounds backwards, but the dashboard design tells you exactly what data to collect. No more, no less.&lt;/p&gt;

&lt;p&gt;I'd also add UTM parameter support from day one. Being able to append UTM tags automatically when creating short links saves a ton of manual work.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://briefly-iota.vercel.app" rel="noopener noreferrer"&gt;Briefly&lt;/a&gt; is free to use. Create short links, track clicks, see where your traffic comes from. No signup wall for basic features.&lt;/p&gt;

&lt;p&gt;The code taught me more about web analytics than any course could. Sometimes the best way to learn is to build the thing.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Generate Open Graph Images Dynamically (No Puppeteer)</title>
      <dc:creator>Narender singh</dc:creator>
      <pubDate>Thu, 26 Mar 2026 13:45:30 +0000</pubDate>
      <link>https://dev.to/narender_singh_6c6e271c67/how-to-generate-open-graph-images-dynamically-no-puppeteer-5ca4</link>
      <guid>https://dev.to/narender_singh_6c6e271c67/how-to-generate-open-graph-images-dynamically-no-puppeteer-5ca4</guid>
      <description>&lt;p&gt;You know what's annoying? Spinning up a headless browser just to generate a social preview image.&lt;/p&gt;

&lt;p&gt;Puppeteer works. Sure. But it's slow, memory-hungry, and a nightmare to deploy on serverless. I spent way too long fighting Chrome binaries on Vercel before I gave up and looked for something better.&lt;/p&gt;

&lt;p&gt;Turns out there's a simpler way.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem With Puppeteer-Based OG Images
&lt;/h2&gt;

&lt;p&gt;Here's the typical flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Spin up a headless Chrome instance&lt;/li&gt;
&lt;li&gt;Render an HTML page with your dynamic content&lt;/li&gt;
&lt;li&gt;Screenshot it&lt;/li&gt;
&lt;li&gt;Serve that screenshot as your OG image&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It works on your local machine. Then you deploy it and everything breaks. Chrome needs specific system libraries. Cold starts take 5-10 seconds. Memory usage spikes. And if you're on a free tier anywhere, good luck.&lt;/p&gt;

&lt;p&gt;I've burned entire weekends on this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter OGPix
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://ogpix-pi.vercel.app" rel="noopener noreferrer"&gt;OGPix&lt;/a&gt; takes a completely different approach. Instead of running a browser, it generates images through an API. You send parameters, you get back an image URL. That's it.&lt;/p&gt;

&lt;p&gt;No Chrome. No Puppeteer. No deployment headaches.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works — Step by Step
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Pick a Template or Build Your Own
&lt;/h3&gt;

&lt;p&gt;OGPix comes with pre-built templates for blog posts, product pages, and documentation. But you can also customize everything.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Generate via URL Parameters
&lt;/h3&gt;

&lt;p&gt;The simplest approach — just construct a URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://ogpix-pi.vercel.app/api/og?title=My+Blog+Post&amp;amp;description=A+quick+tutorial&amp;amp;theme=dark
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Drop that into your meta tags:&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://ogpix-pi.vercel.app/api/og?title=My+Blog+Post&amp;amp;description=A+quick+tutorial&amp;amp;theme=dark"&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;name=&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="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. Use It in Next.js
&lt;/h3&gt;

&lt;p&gt;If you're on Next.js, add it to your layout or page head:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&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="o"&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;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="err"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;`https://ogpix-pi.vercel.app/api/og?title=&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;{encodeURIComponent(post.title)}&lt;/span&gt;&lt;span class="se"&gt;\`&lt;/span&gt;&lt;span class="s2"&gt;,
        width: 1200,
        height: 630,
      },
    ],
  },
};
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Dynamic Routes? No Problem
&lt;/h3&gt;

&lt;p&gt;For blog posts or any dynamic content, just swap in the variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getOGImage&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;author&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;params&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;URLSearchParams&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;author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gradient&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="k"&gt;return&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;`https://ogpix-pi.vercel.app/api/og?&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;{params.toString()}&lt;/span&gt;&lt;span class="se"&gt;\`&lt;/span&gt;&lt;span class="s2"&gt;;
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why This Beats Puppeteer
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Speed&lt;/strong&gt;: Images generate in milliseconds, not seconds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No dependencies&lt;/strong&gt;: Zero system libraries to install&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serverless-friendly&lt;/strong&gt;: Works everywhere, including edge functions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching&lt;/strong&gt;: URLs are deterministic, so CDN caching just works&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free&lt;/strong&gt;: No paid tiers for basic usage&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Results
&lt;/h2&gt;

&lt;p&gt;I switched three of my projects from Puppeteer to OGPix. Deploy times dropped. No more random failures in CI. And the images actually look better because I'm not fighting CSS rendering inconsistencies across headless browsers.&lt;/p&gt;

&lt;p&gt;If you're still wrestling with Puppeteer for OG images, stop. There's a better way now.&lt;/p&gt;

&lt;p&gt;Check it out at &lt;a href="https://ogpix-pi.vercel.app" rel="noopener noreferrer"&gt;ogpix-pi.vercel.app&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>seo</category>
      <category>nextjs</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>I built a free URL shortener with built-in analytics — here's why</title>
      <dc:creator>Narender singh</dc:creator>
      <pubDate>Wed, 25 Mar 2026 09:49:12 +0000</pubDate>
      <link>https://dev.to/narender_singh_6c6e271c67/i-built-a-free-url-shortener-with-built-in-analytics-heres-why-4ebi</link>
      <guid>https://dev.to/narender_singh_6c6e271c67/i-built-a-free-url-shortener-with-built-in-analytics-heres-why-4ebi</guid>
      <description>&lt;p&gt;Every few months I find myself needing a short link. Maybe it's for a newsletter, a tweet, or a slide deck. So I go to one of the popular URL shorteners and... immediately regret it.&lt;/p&gt;

&lt;p&gt;Bitly wants $35/month if you want to see where your clicks come from. TinyURL barely tells you anything. The free ones plaster ads everywhere. And half of them look like they were built in 2011.&lt;/p&gt;

&lt;p&gt;I got tired of it. So I built my own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meet Briefly
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://briefly-iota.vercel.app" rel="noopener noreferrer"&gt;Briefly&lt;/a&gt; is a URL shortener with built-in click analytics. No ads. No signup walls just to create a link. And you can actually &lt;em&gt;see&lt;/em&gt; who's clicking your links without paying enterprise pricing.&lt;/p&gt;

&lt;p&gt;Here's what it does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shorten any URL&lt;/strong&gt; — clean, short links you can share anywhere&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Click analytics&lt;/strong&gt; — track clicks by country, device, browser, and referrer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dashboard with charts&lt;/strong&gt; — not just a number in a table, actual visual breakdowns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom slugs&lt;/strong&gt; — pick your own short code if you want&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;QR codes&lt;/strong&gt; — because apparently people still scan those (they do, I checked my own analytics)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Dashboard
&lt;/h2&gt;

&lt;p&gt;The main dashboard shows all your links in one place. You get total clicks, a sparkline for recent activity, and quick actions to copy or edit. Nothing fancy, just clean and functional.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbriefly-iota.vercel.app" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbriefly-iota.vercel.app" alt="Dashboard view showing links list with click counts and sparklines" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Analytics Page
&lt;/h2&gt;

&lt;p&gt;This is the part I'm most proud of. Click on any link and you get a full breakdown:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Click timeline&lt;/strong&gt; — see when people are clicking (turns out most of my traffic hits between 9-11am EST)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Geographic map&lt;/strong&gt; — clicks by country, color-coded&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Device split&lt;/strong&gt; — desktop vs mobile vs tablet&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browser breakdown&lt;/strong&gt; — Chrome dominates, obviously, but it's interesting to see the Firefox and Safari numbers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Top referrers&lt;/strong&gt; — where the traffic is actually coming from&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbriefly-iota.vercel.app" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbriefly-iota.vercel.app" alt="Analytics page with charts for clicks over time, geography, devices, and referrers" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;p&gt;I wanted to keep this accessible. Here's the deal:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plan&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Links/month&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;100 links&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pro&lt;/td&gt;
&lt;td&gt;$9/mo&lt;/td&gt;
&lt;td&gt;1,000 links&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Business&lt;/td&gt;
&lt;td&gt;$29/mo&lt;/td&gt;
&lt;td&gt;Unlimited&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The free tier isn't some crippled demo. You get full analytics on every link. 100 links/month is plenty for most indie devs and small projects. Pro and Business just give you more volume and some extras like team access.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;p&gt;For the nerds (hi, that's me too):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js 15&lt;/strong&gt; — App Router, server components, the whole deal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supabase&lt;/strong&gt; — auth, database, real-time subscriptions for live click tracking&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vercel Edge&lt;/strong&gt; — redirect handling runs at the edge so your short links resolve fast. Like, really fast. I'm seeing sub-50ms redirects in most regions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recharts&lt;/strong&gt; — for the dashboard visualizations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I went with Supabase over a custom Postgres setup because honestly the auth and row-level security saved me probably two weeks of work. Sometimes the boring choice is the right choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not just use Bitly?
&lt;/h2&gt;

&lt;p&gt;Look, Bitly is fine if you're a marketing team with budget. But I'm a solo dev who just wants to share links and know if anyone actually clicks them. I don't need campaign management or "branded domains" or whatever. I need a box where I paste a URL and get a short one back, with a chart that shows me what happened after.&lt;/p&gt;

&lt;p&gt;That's it. That's the whole product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other tools I've built
&lt;/h2&gt;

&lt;p&gt;Briefly is part of a small suite of dev tools I've been putting together. If you're into this kind of thing, check these out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://ogpix-pi.vercel.app" rel="noopener noreferrer"&gt;OGPix&lt;/a&gt;&lt;/strong&gt; — generate Open Graph images via API. Drop in a URL and get a social preview image back. Super handy for dynamic OG tags.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://pingbase-sigma.vercel.app" rel="noopener noreferrer"&gt;PingBase&lt;/a&gt;&lt;/strong&gt; — uptime monitoring for your sites. Get pinged when something goes down. I use it to monitor all my own projects, including Briefly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://qrgen-lilac-tau.vercel.app" rel="noopener noreferrer"&gt;QRGen&lt;/a&gt;&lt;/strong&gt; — QR code generator. Clean, customizable QR codes. I actually use this &lt;em&gt;inside&lt;/em&gt; Briefly for the QR code feature, which is kind of meta.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it out
&lt;/h2&gt;

&lt;p&gt;Go to &lt;a href="https://briefly-iota.vercel.app" rel="noopener noreferrer"&gt;https://briefly-iota.vercel.app&lt;/a&gt;, paste a link, and shorten it. Takes about 3 seconds.&lt;/p&gt;

&lt;p&gt;If you run into bugs or have feature ideas, I genuinely want to hear about it. Drop a comment here or open an issue. I'm building this stuff because I use it myself every day, so feedback from other devs is gold.&lt;/p&gt;

&lt;p&gt;And if you've built something similar, I'd love to hear about your approach too. Always curious how other people solve this stuff.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>20 Free Developer Tools That Run Entirely in Your Browser</title>
      <dc:creator>Narender singh</dc:creator>
      <pubDate>Wed, 25 Mar 2026 08:15:49 +0000</pubDate>
      <link>https://dev.to/narender_singh_6c6e271c67/20-free-developer-tools-that-run-entirely-in-your-browser-2h63</link>
      <guid>https://dev.to/narender_singh_6c6e271c67/20-free-developer-tools-that-run-entirely-in-your-browser-2h63</guid>
      <description>&lt;p&gt;I've been building browser-based developer tools for the past year or so. What started as a single OG image generator turned into a full collection of 20 free utilities. They all run in your browser, which means your data never hits a server.&lt;/p&gt;

&lt;p&gt;I want to walk through all of them, grouped by what you'd actually use them for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why browser-only tools matter
&lt;/h2&gt;

&lt;p&gt;Speed is the obvious reason. There's no round trip to a server, no waiting for a response. Paste your text, get your result.&lt;/p&gt;

&lt;p&gt;But the real reason is privacy. Think about what developers paste into online tools. JWT tokens with user data. API keys in Base64 strings. Config files with database credentials. Password hashes. Every time you use a server-side tool, that data leaves your machine and lands on infrastructure you don't control.&lt;/p&gt;

&lt;p&gt;These tools use the Web Crypto API, TextEncoder, and native browser APIs to do everything locally. Your input stays in your browser tab and nowhere else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging and data inspection
&lt;/h2&gt;

&lt;p&gt;When something breaks, you need answers fast. You don't want to context-switch into a terminal or install a CLI tool just to decode a token.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://ogpix-pi.vercel.app/jwt-decoder" rel="noopener noreferrer"&gt;JWT Decoder&lt;/a&gt; splits your token into header, payload, and signature. It shows expiration status, issued-at timestamps, and all claims with color coding so expired tokens are obvious at a glance. I built this after realizing most online JWT decoders quietly send your token to their backend.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://ogpix-pi.vercel.app/diff-checker" rel="noopener noreferrer"&gt;Diff Checker&lt;/a&gt; does side-by-side text comparison with line-level highlighting. I used to rely on a bookmarked tool for this, but it started requiring a login. Comparing config files, checking what changed in an API response, diffing two versions of a function. All common tasks that shouldn't require an account.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://ogpix-pi.vercel.app/regex-tester" rel="noopener noreferrer"&gt;Regex Tester&lt;/a&gt; lets you write patterns and test them against sample text in real time. Matches are highlighted as you type. It beats opening a Node REPL every time you need to validate a pattern.&lt;/p&gt;

&lt;p&gt;Then there's the &lt;a href="https://jsonprettify-ruby.vercel.app" rel="noopener noreferrer"&gt;JSON Formatter&lt;/a&gt;. Paste in minified or messy JSON and get it formatted with syntax highlighting. I probably use this one more than any other tool on the list.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://ogpix-pi.vercel.app/csv-to-json" rel="noopener noreferrer"&gt;CSV to JSON Converter&lt;/a&gt; handles the tedious work of transforming CSV exports into JSON. Supports custom delimiters. I originally built it because I kept getting CSV exports from Stripe and needed them in a different format for scripts I was writing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security, encoding, and hashing
&lt;/h2&gt;

&lt;p&gt;This category is where the privacy argument hits hardest. You really shouldn't be pasting passwords and tokens into tools that phone home.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://ogpix-pi.vercel.app/hash-generator" rel="noopener noreferrer"&gt;Hash Generator&lt;/a&gt; supports SHA-256, SHA-512, SHA-1, and MD5. It uses SubtleCrypto.digest() under the hood, so the browser does all the computation. I use it for verifying file integrity and generating test data.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://ogpix-pi.vercel.app/base64" rel="noopener noreferrer"&gt;Base64 Encoder&lt;/a&gt; handles both text and files. It auto-detects whether your input is already encoded, which saves a step. Simple tool, but I reach for it constantly.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://ogpix-pi.vercel.app/url-encoder" rel="noopener noreferrer"&gt;URL Encoder/Decoder&lt;/a&gt; encodes and decodes URL components. Useful when you're debugging query parameters or building URLs programmatically and something looks off.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://ogpix-pi.vercel.app/password-generator" rel="noopener noreferrer"&gt;Password Generator&lt;/a&gt; creates strong passwords with configurable length and character sets. Generated entirely in the browser using crypto.getRandomValues(). No server ever sees the password.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://ogpix-pi.vercel.app/uuid-generator" rel="noopener noreferrer"&gt;UUID Generator&lt;/a&gt; produces v4 UUIDs on demand. Copy with one click. I use this when seeding databases or writing tests that need unique identifiers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Content, SEO, and social sharing
&lt;/h2&gt;

&lt;p&gt;If you ship anything on the web, you've dealt with broken link previews. These tools exist because I got tired of deploying, sharing a link, and seeing a blank preview card on Twitter.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://ogpix-pi.vercel.app/playground" rel="noopener noreferrer"&gt;OG Image Generator&lt;/a&gt; is where this whole project started. Design Open Graph images visually with templates, custom text, and backgrounds. Export as PNG. No Figma needed for basic social cards.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://ogpix-pi.vercel.app/checker" rel="noopener noreferrer"&gt;OG Tag Checker&lt;/a&gt; lets you enter a URL and see exactly what meta tags are present, what's missing, and how your link preview will look on different platforms. Saves you from the deploy-and-pray cycle.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://ogpix-pi.vercel.app/meta-generator" rel="noopener noreferrer"&gt;Meta Tag Generator&lt;/a&gt; produces the full set of og:title, og:description, twitter:card, and related tags. Fill in the fields, copy the HTML. It covers the tags you forget exist until your link preview looks wrong on LinkedIn.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://pagepulse-iota.vercel.app/privacy-policy-generator" rel="noopener noreferrer"&gt;Privacy Policy Generator&lt;/a&gt; creates a privacy policy page based on your inputs. Useful for side projects and MVPs where you need a policy but don't want to pay a lawyer for a hobby project.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://ogpix-pi.vercel.app/markdown-preview" rel="noopener noreferrer"&gt;Markdown Previewer&lt;/a&gt; renders Markdown in real time as you type. I use it for previewing README files and blog post drafts before pushing them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Visual and utility tools
&lt;/h2&gt;

&lt;p&gt;The remaining tools handle visual tasks and quick conversions that come up during development.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://ogpix-pi.vercel.app/favicon" rel="noopener noreferrer"&gt;Favicon Generator&lt;/a&gt; creates favicons from images or text. Exports in multiple sizes. Every new project needs one and this is faster than opening an image editor.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://ogpix-pi.vercel.app/resizer" rel="noopener noreferrer"&gt;Image Resizer&lt;/a&gt; resizes images to specific dimensions. Runs entirely in the browser using Canvas API. No upload, no compression artifacts from a third-party service.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://ogpix-pi.vercel.app/screenshot" rel="noopener noreferrer"&gt;Screenshot Tool&lt;/a&gt; captures screenshots of any URL. Handy for documentation, creating preview images, or checking how a page renders without navigating to it.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://ogpix-pi.vercel.app/color-converter" rel="noopener noreferrer"&gt;Color Converter&lt;/a&gt; converts between HEX, RGB, HSL, and other color formats. Includes a visual picker. Small tool, but useful when you're jumping between CSS and design specs.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://qrgen-lilac-tau.vercel.app" rel="noopener noreferrer"&gt;QR Code Generator&lt;/a&gt; generates QR codes from any text or URL. Download as PNG. I've used it for event links and quick sharing between devices.&lt;/p&gt;

&lt;h2&gt;
  
  
  The technical side
&lt;/h2&gt;

&lt;p&gt;Everything is built with Next.js and statically rendered. There are no API routes powering these tools. The cryptographic operations use the Web Crypto API. The image tools use Canvas. The diffing uses a longest common subsequence algorithm. No heavy dependencies for the core tool logic.&lt;/p&gt;

&lt;p&gt;The tools load fast because there's very little to load. No analytics scripts, no third-party tracking, no cookie banners.&lt;/p&gt;

&lt;h2&gt;
  
  
  All 20 tools in one place
&lt;/h2&gt;

&lt;p&gt;You can find everything at &lt;a href="https://ogpix-pi.vercel.app" rel="noopener noreferrer"&gt;ogpix-pi.vercel.app&lt;/a&gt;. Free, no account required, no data collection. If something's broken or you want a tool I haven't built, let me know in the comments.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tools</category>
      <category>productivity</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Your link previews look broken. Here's a one-line fix.</title>
      <dc:creator>Narender singh</dc:creator>
      <pubDate>Wed, 25 Mar 2026 07:42:52 +0000</pubDate>
      <link>https://dev.to/narender_singh_6c6e271c67/your-link-previews-look-broken-heres-a-one-line-fix-49i7</link>
      <guid>https://dev.to/narender_singh_6c6e271c67/your-link-previews-look-broken-heres-a-one-line-fix-49i7</guid>
      <description>&lt;p&gt;Your link previews on Twitter and LinkedIn probably look broken. Or they show your favicon at 50x50 pixels. Or there's no image at all and the link gets scrolled past.&lt;/p&gt;

&lt;p&gt;This happens because you either don't have an og:image meta tag, or it points to something that wasn't designed for 1200x630.&lt;/p&gt;

&lt;p&gt;I fixed this for my own projects by building an API that generates the image from URL parameters. No Figma, no uploading files, no manual work per page.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Your meta tag looks like this:&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://ogpix-pi.vercel.app/api/og?title=My+Blog+Post&amp;amp;theme=dark"&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;When someone shares your link, the platform hits that URL, gets back a 1200x630 PNG, and your preview looks good. The image is generated on the fly from the query parameters.&lt;/p&gt;

&lt;p&gt;You can set the title, description, and pick from 10 themes. The images get cached at the CDN edge, so after the first request it's basically instant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next.js example
&lt;/h2&gt;

&lt;p&gt;If you're using Next.js with the app router, you can generate unique OG images per page automatically:&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;// 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;ogUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;`https://ogpix-pi.vercel.app/api/og?title=&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;{encodeURIComponent(post.title)}&amp;amp;theme=minimal&lt;/span&gt;&lt;span class="se"&gt;\`&lt;/span&gt;&lt;span class="s2"&gt;;

  return {
    openGraph: {
      images: [{ url: ogUrl, width: 1200, height: 630 }],
    },
  };
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every blog post gets its own OG image with the correct title. Zero manual work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about Vercel's built-in OG?
&lt;/h2&gt;

&lt;p&gt;Vercel has &lt;code&gt;@vercel/og&lt;/code&gt; which uses Satori to render React components as images. It works, but you have to write JSX for your image layout, deal with font loading, handle edge cases with CSS support, and deploy it as part of your app.&lt;/p&gt;

&lt;p&gt;With an external API you just set a URL. Works with any framework, any hosting provider, any static site. One line of HTML.&lt;/p&gt;

&lt;h2&gt;
  
  
  The free tools
&lt;/h2&gt;

&lt;p&gt;While building this I also made a bunch of free developer tools that run in the browser:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://ogpix-pi.vercel.app/checker" rel="noopener noreferrer"&gt;OG Tag Checker&lt;/a&gt; - see how your links look on social media&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ogpix-pi.vercel.app/hash-generator" rel="noopener noreferrer"&gt;Hash Generator&lt;/a&gt; - SHA-256, MD5, etc.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ogpix-pi.vercel.app/jwt-decoder" rel="noopener noreferrer"&gt;JWT Decoder&lt;/a&gt; - decode tokens without sending them to a server&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ogpix-pi.vercel.app/regex-tester" rel="noopener noreferrer"&gt;Regex Tester&lt;/a&gt; - test patterns with live highlighting&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ogpix-pi.vercel.app/password-generator" rel="noopener noreferrer"&gt;Password Generator&lt;/a&gt; - cryptographically random passwords&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All client-side, no tracking, no accounts needed. Full list at &lt;a href="https://ogpix-pi.vercel.app/tools" rel="noopener noreferrer"&gt;ogpix-pi.vercel.app/tools&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The playground lets you preview all themes and copy the URL: &lt;a href="https://ogpix-pi.vercel.app/playground" rel="noopener noreferrer"&gt;ogpix-pi.vercel.app/playground&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Free tier is 100 images per month. If you're running a blog or side project, that's probably enough. Paid plans exist for higher volume.&lt;/p&gt;

&lt;p&gt;If something doesn't work or you want a feature added, leave a comment.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>seo</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>I built 7 browser-only dev tools because I got tired of ads and data collection</title>
      <dc:creator>Narender singh</dc:creator>
      <pubDate>Wed, 25 Mar 2026 05:57:00 +0000</pubDate>
      <link>https://dev.to/narender_singh_6c6e271c67/i-built-7-browser-only-dev-tools-because-i-got-tired-of-ads-and-data-collection-n5b</link>
      <guid>https://dev.to/narender_singh_6c6e271c67/i-built-7-browser-only-dev-tools-because-i-got-tired-of-ads-and-data-collection-n5b</guid>
      <description>&lt;p&gt;Every few months I end up on some random website trying to decode a JWT or hash a string, and it's the same experience every time. Cookie banners, ads covering half the page, and that nagging feeling that my data just got sent to someone's server.&lt;/p&gt;

&lt;p&gt;So I built my own versions. Seven of them, actually.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with most online dev tools
&lt;/h2&gt;

&lt;p&gt;Most of these tools are simple operations. Base64 encoding is a one-liner in any language. Hashing a string takes three lines of JavaScript with the Web Crypto API. But when you're debugging at midnight and just need a quick answer, you reach for a browser tool.&lt;/p&gt;

&lt;p&gt;And almost every one of them sends your input to a server. I checked. Paste a JWT into most "JWT decoders" online and watch your network tab. That token, which might contain user IDs, emails, permissions, is getting shipped off somewhere. For a decode operation that requires zero server involvement.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;p&gt;All of these run entirely in your browser. No backend calls, no data leaving your machine. They're part of &lt;a href="https://ogpix-pi.vercel.app/tools" rel="noopener noreferrer"&gt;OGPix&lt;/a&gt;, which started as an OG image generator but turned into a collection of developer utilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hash generator&lt;/strong&gt; - SHA-256, SHA-512, SHA-1, MD5. Paste text, get hashes. I use this mostly for verifying file integrity and generating test data. Uses the Web Crypto API so everything stays local.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JWT decoder&lt;/strong&gt; - Splits the token into header, payload, and signature. Shows expiration status, issued-at time, all the claims. Color coded so you can spot expired tokens fast. No server touch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diff checker&lt;/strong&gt; - Side by side text comparison with line-level highlighting. I was using an old bookmarked tool for this that started requiring a login last month. So I made my own.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Base64 encoder/decoder&lt;/strong&gt; - Handles text and files. Detects if your input is already encoded. Simple, but I use it more than I'd like to admit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CSV to JSON converter&lt;/strong&gt; - Drop in a CSV, get JSON out. Supports custom delimiters. I built this after manually converting CSV exports from Stripe for the third time in a week.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Meta tag generator&lt;/strong&gt; - Fills in og:title, og:description, twitter:card, and all the other tags you forget exist until your link preview looks broken on LinkedIn. Generates the HTML you can copy paste.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Screenshot tool&lt;/strong&gt; - Enter a URL, get a screenshot. Useful for preview images, documentation, or checking how a page looks without opening it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why browser-only matters
&lt;/h2&gt;

&lt;p&gt;I keep coming back to this because it actually matters for developer tools specifically. We paste API keys into JWT decoders. We hash passwords to test authentication flows. We diff config files with credentials in them.&lt;/p&gt;

&lt;p&gt;When a tool runs client side, none of that leaves your machine. When it runs server side, you're trusting a stranger's infrastructure with your data. For something that takes 10 milliseconds to compute locally.&lt;/p&gt;

&lt;p&gt;The Web Crypto API handles all the hashing. TextEncoder and atob/btoa handle Base64. JWT decoding is just splitting on dots and parsing Base64. None of this needs a server.&lt;/p&gt;

&lt;h2&gt;
  
  
  The technical bits
&lt;/h2&gt;

&lt;p&gt;Everything is Next.js, statically rendered. No API routes for any of these tools. The hash generator uses SubtleCrypto.digest(), the diff checker uses a longest common subsequence algorithm, the CSV parser handles quoted fields and custom delimiters.&lt;/p&gt;

&lt;p&gt;The whole thing loads fast because there's nothing to load. No analytics scripts, no third party dependencies for the tool logic. Your browser does the work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try them
&lt;/h2&gt;

&lt;p&gt;All the tools are at &lt;a href="https://ogpix-pi.vercel.app/tools" rel="noopener noreferrer"&gt;ogpix-pi.vercel.app/tools&lt;/a&gt;. Free, no account, no tracking. If something is broken or you want a tool I haven't built yet, tell me in the comments.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>tools</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Stop Copying Privacy Policies from Other Websites</title>
      <dc:creator>Narender singh</dc:creator>
      <pubDate>Wed, 25 Mar 2026 05:28:21 +0000</pubDate>
      <link>https://dev.to/narender_singh_6c6e271c67/stop-copying-privacy-policies-from-other-websites-3bp9</link>
      <guid>https://dev.to/narender_singh_6c6e271c67/stop-copying-privacy-policies-from-other-websites-3bp9</guid>
      <description>&lt;p&gt;I see this all the time. Someone launches a side project, realizes they need a privacy policy, and just copies one from a bigger site. Swap out the company name, maybe change a few details, call it done.&lt;/p&gt;

&lt;p&gt;This is a terrible idea for a few reasons.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can actually go wrong
&lt;/h2&gt;

&lt;p&gt;First, privacy policies reference specific data practices. If you copied yours from a site that uses Stripe, Google Analytics, and Mailchimp but you only use Stripe, your policy is lying to your users about what services touch their data. That's not just sloppy. Under GDPR, it can get you fined.&lt;/p&gt;

&lt;p&gt;Second, jurisdiction matters. A privacy policy written for a company based in California has CCPA-specific language. If you're operating out of Germany, that policy is missing half the GDPR requirements and includes stuff that doesn't apply to you. It's the wrong document.&lt;/p&gt;

&lt;p&gt;Third, and people forget this one, privacy policies can be copyrighted. The text itself is creative work. Copying it wholesale could technically be infringement. Nobody's gotten sued over this yet as far as I know, but it's not a great foundation for your legal compliance.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a privacy policy actually needs
&lt;/h2&gt;

&lt;p&gt;At a minimum, you need to cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What data you collect&lt;/strong&gt; (emails, names, cookies, IP addresses, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why you collect it&lt;/strong&gt; (account creation, analytics, marketing)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who you share it with&lt;/strong&gt; (third-party services, payment processors)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How users can control their data&lt;/strong&gt; (deletion requests, opt-outs)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your contact information&lt;/strong&gt; for privacy-related questions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have users in the EU, GDPR requires you to list lawful bases for processing, data retention periods, and information about cross-border transfers. If you have users in California, CCPA gives them the right to know what's collected and to request deletion.&lt;/p&gt;

&lt;p&gt;This isn't something you can fake by copying someone else's homework.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I built instead
&lt;/h2&gt;

&lt;p&gt;I got tired of pointing friends to expensive legal template sites or watching them copy-paste from random companies. So I built a free privacy policy generator that actually asks you the right questions.&lt;/p&gt;

&lt;p&gt;You tell it what data you collect, what services you use, where you're based, and it generates a proper template. It covers GDPR and CCPA basics and gives you something that actually reflects your app.&lt;/p&gt;

&lt;p&gt;You can try it here: &lt;a href="https://pagepulse-iota.vercel.app/privacy-policy-generator" rel="noopener noreferrer"&gt;Privacy Policy Generator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also built a &lt;a href="https://pagepulse-iota.vercel.app/terms-generator" rel="noopener noreferrer"&gt;Terms of Service Generator&lt;/a&gt; that works the same way. Both are free, no signup required.&lt;/p&gt;

&lt;h2&gt;
  
  
  A quick disclaimer
&lt;/h2&gt;

&lt;p&gt;These generators produce templates, not legal advice. If you're handling sensitive data like health records or financial information, talk to an actual lawyer. But for most side projects and small apps, a well-structured template beats a copy-pasted policy that doesn't match your product.&lt;/p&gt;

&lt;p&gt;The tools are part of &lt;a href="https://pagepulse-iota.vercel.app" rel="noopener noreferrer"&gt;PagePulse&lt;/a&gt;, a set of free utilities I'm putting together for people launching websites. No accounts, no paywalls. Just tools that do the job.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>startup</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
