<?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: Abid niazi</title>
    <description>The latest articles on DEV Community by Abid niazi (@abid_niazi_a7faa6246a7952).</description>
    <link>https://dev.to/abid_niazi_a7faa6246a7952</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3894279%2F1c0050ad-e1a9-472a-891b-955def09cd9e.png</url>
      <title>DEV Community: Abid niazi</title>
      <link>https://dev.to/abid_niazi_a7faa6246a7952</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/abid_niazi_a7faa6246a7952"/>
    <language>en</language>
    <item>
      <title>I built a zero-gate, open-access dashboard for SaaS Unit Economics (and other core utilities) using Next.js 14 App Router</title>
      <dc:creator>Abid niazi</dc:creator>
      <pubDate>Mon, 13 Jul 2026 05:41:41 +0000</pubDate>
      <link>https://dev.to/abid_niazi_a7faa6246a7952/i-built-a-zero-gate-open-access-dashboard-for-saas-unit-economics-and-other-core-utilities-using-2n1k</link>
      <guid>https://dev.to/abid_niazi_a7faa6246a7952/i-built-a-zero-gate-open-access-dashboard-for-saas-unit-economics-and-other-core-utilities-using-2n1k</guid>
      <description>&lt;p&gt;Hey dev.to community!&lt;/p&gt;

&lt;p&gt;As developers and builders, we love building products, but we often stumble when it comes to tracking the actual business metrics behind them. A few days ago, I got frustrated seeing early-stage founders and indie hackers calculate their Customer Lifetime Value (LTV) incorrectly.&lt;/p&gt;

&lt;p&gt;Most people use the classic:&lt;br&gt;
LTV = ARPU / Churn&lt;/p&gt;

&lt;p&gt;The problem? It completely omits Gross Margin. If your monthly ARPU is $100 but your infrastructure, third-party APIs, and customer success overhead leave you with a 65% gross margin, your true LTV is vastly different. If you spend $500 on CAC thinking your LTV is $400, your payback runway stretches into a dangerous cash-flow chokehold.&lt;/p&gt;

&lt;p&gt;To fix this, I engineered an open-access, zero-signup SaaS LTV:CAC Payback Dashboard alongside a suite of other precise calculators on my platform, FreeToolForge.&lt;/p&gt;

&lt;p&gt;🛠️ The Core Tools Built&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;SaaS LTV:CAC Payback Dashboard
This tool isolates your software gross margins and visually models your exact customer payback timeline on a dynamic Tailwind-styled risk index (Excellent, Good, Warning, Danger). It’s designed to help you stress-test your startup unit economics before building an investor deck.
👉 Try it here: SaaS LTV:&lt;a href="https://freetoolforge.org/tools/calculators/saas-churn-calculator" rel="noopener noreferrer"&gt;https://freetoolforge.org/tools/calculators/saas-churn-calculator&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;🇩🇪 2. German GPA (Modified Bavarian Formula) Calculator&lt;br&gt;
For developers or students looking to relocate or apply for English-taught master's degrees in Germany, converting foreign grading scales is a massive pain. This tool implements the exact mathematical formula used by uni-assist to invert and map grades down to the German 1.0 to 4.0 scale with single-decimal precision.&lt;br&gt;
👉 Try it here: &lt;a href="https://freetoolforge.org/tools/calculators/german-gpa-bavarian-formula-calculator" rel="noopener noreferrer"&gt;https://freetoolforge.org/tools/calculators/german-gpa-bavarian-formula-calculator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🏗️ The Tech Stack &amp;amp; Architecture Under the Hood&lt;br&gt;
I wanted the platform to be blazing fast and completely optimized for search crawlers, so I chose a modern frontend architecture:&lt;/p&gt;

&lt;p&gt;Framework: Next.js (App Router) using a strict single-page paradigm. The interactive calculator tools live as client components ('use client'), while massive 3,000+ word technical documentation and semantic guides sit directly underneath them to ensure rich HTML parsing.&lt;/p&gt;

&lt;p&gt;Styling: Tailwind CSS with full configuration for class-based dark mode (dark:) tied to a global context provider.&lt;/p&gt;

&lt;p&gt;Performance: All tools leverage static generation hooks where applicable to guarantee near-instantaneous load times on mobile viewports.&lt;/p&gt;

&lt;p&gt;🛑 Why No Signup Walls?&lt;br&gt;
We’ve all experienced the frustration of using a simple online calculator only to be hit with a "Enter your email to see results" wall. I am actively building FreeToolForge as an anti-lead-gen platform. The goal is pure, frictionless utility. You plug in your data, the browser computes the state, and you get your answer instantly.&lt;/p&gt;

&lt;p&gt;I’d love to get your feedback on the UI/UX, responsiveness, or any additional features you'd want to see on the SaaS economic dashboard (like adding dynamic multi-cohort decay curves!).&lt;/p&gt;

&lt;p&gt;Let me know what you think in the comments below! 👇&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Breakdown: Hitting the new 350-point combat standard for 2026 without destroying your lower back on the MDL.</title>
      <dc:creator>Abid niazi</dc:creator>
      <pubDate>Tue, 07 Jul 2026 15:20:20 +0000</pubDate>
      <link>https://dev.to/abid_niazi_a7faa6246a7952/breakdown-hitting-the-new-350-point-combat-standard-for-2026-without-destroying-your-lower-back-on-5615</link>
      <guid>https://dev.to/abid_niazi_a7faa6246a7952/breakdown-hitting-the-new-350-point-combat-standard-for-2026-without-destroying-your-lower-back-on-5615</guid>
      <description>&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="freetoolforge.org/tools/calculators/us-army-acft-calculator" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;freetoolforge.org/tools/calculators/us-army-acft-calculator&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
1. With the shift to the new baseline standards this year, a lot of soldiers are stressing about the 350-point total requirement for combat specialties. You still only need a minimum of 60 per event to pass, but the math changes if you need that 350 total.  You essentially need to average 58.3 points across the board. The mistake I keep seeing at the company level is guys trying to overcompensate on the 3-Rep Max Deadlift (MDL) and risking an injury.  Look at the scoring curve: it is exponentially easier to squeeze an extra 10 points out of the Sprint-Drag-Carry (SDC) by just cleaning up your sled turn transitions than it is to add another 20lbs to your hex bar. Same goes for the Plank. Locking in an extra 20 seconds on your plank gets you further up the chart than smoking yourself on the run.  Work smarter, not harder. Maximize the events where form yields free time/reps, and coast on the 60-point minimum for the ones that wreck your joints.(Edit: Since people are asking how to calculate exactly how many seconds/pounds they need to hit the 350 mark across different events, I put together a [freetoolforge.org/tools/calculators/us-army-acft-calculator]. It lets you slide the numbers to see where you can make up the easiest points).

</description>
    </item>
    <item>
      <title>Stop calculating your UCAS points wrong (Your AS levels do NOT stack with your A-Levels).</title>
      <dc:creator>Abid niazi</dc:creator>
      <pubDate>Tue, 07 Jul 2026 15:18:43 +0000</pubDate>
      <link>https://dev.to/abid_niazi_a7faa6246a7952/stop-calculating-your-ucas-points-wrong-your-as-levels-do-not-stack-with-your-a-levels-2m48</link>
      <guid>https://dev.to/abid_niazi_a7faa6246a7952/stop-calculating-your-ucas-points-wrong-your-as-levels-do-not-stack-with-your-a-levels-2m48</guid>
      <description>&lt;p&gt;Seeing a lot of panicking in the threads right now about university offers, so here is a quick breakdown of how the Tariff system actually works for 2026 entry.  1. AS Levels do not stack: If you take an AS level in a subject and then complete the full A-Level in that same subject, you ONLY get the points for the A-Level. Do not add them together.  2. EPQs are a cheat code: An Extended Project Qualification (EPQ) is worth up to 28 UCAS points (equivalent to half an A-Level). If your firm choice asks for 120 points and you are looking at BBB (which is 120), dropping to a BBC (112 points) but securing a solid EPQ grade can still get you over the line.  3. BTECs are heavy hitters: A Distinction* (D*) in a BTEC is worth 56 points, which is exactly the same as an A* at A-Level. If you are doing a mix, factor that in.  Universities don't care how you get the points if they make a Tariff offer, they just care about the total number.  (Edit: For everyone asking how to mix and match BTEC DD grades with A-Levels to see if they meet their firm offer, I coded a quick [&lt;a href="https://freetoolforge.org/tools/calculators/uk-ucas-tariff-calculator" rel="noopener noreferrer"&gt;https://freetoolforge.org/tools/calculators/uk-ucas-tariff-calculator&lt;/a&gt;] that lets you combine different qualifications easily).*&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Built a Free Suite of 10+ Online Text &amp; AI Utilities for Developers (No Sign-ups, No Limits!)</title>
      <dc:creator>Abid niazi</dc:creator>
      <pubDate>Wed, 01 Jul 2026 06:28:37 +0000</pubDate>
      <link>https://dev.to/abid_niazi_a7faa6246a7952/built-a-free-suite-of-10-online-text-ai-utilities-for-developers-no-sign-ups-no-limits-3clg</link>
      <guid>https://dev.to/abid_niazi_a7faa6246a7952/built-a-free-suite-of-10-online-text-ai-utilities-for-developers-no-sign-ups-no-limits-3clg</guid>
      <description>&lt;h2&gt;
  
  
  Here is a high-converting, markdown-formatted Dev.to post template designed to launch your text utility platform, engage developers, and build high-quality contextual backlinks.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  🚀 Built a Free Suite of 10+ Online Text &amp;amp; AI Utilities for Developers (No Sign-ups, No Limits!)
&lt;/h2&gt;

&lt;p&gt;Hey DEV community! 👋&lt;br&gt;
As developers, writers, and power users, we manipulate text constantly. Whether it's stripping weird formatting, changing letter cases, formatting JSON, or trying to make sense of a dense 40-page contract, opening heavy software or dealing with ad-choked utility sites is a massive pain.&lt;br&gt;
That’s why I built &lt;a href="https://freetoolforge.org/" rel="noopener noreferrer"&gt;FreeToolForge&lt;/a&gt; — a completely free, fast, and lightweight hub packed with essential text utilities and AI helpers. No account required, no usage limits, just pure utility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Here is a breakdown of what I built and the core tool I'm most excited to share with you all.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  🔥 The Spotlight: AI Contract Simplifier
&lt;/h2&gt;

&lt;p&gt;Reading through terms of service or enterprise agreements is exhausting. I built the AI Contract Simplifier to solve this.&lt;br&gt;
You can paste dense legal agreements, and the AI instantly extracts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parties Involved (Who is signing)&lt;/li&gt;
&lt;li&gt;Primary Obligations (What you actually have to do)&lt;/li&gt;
&lt;li&gt;Financial Terms (Fees, hidden costs)&lt;/li&gt;
&lt;li&gt;Key Deadlines &amp;amp; Renewal Conditions (So you don't get trapped)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  It translates aggressive legal jargon into plain, actionable English. I would love for some of the developers here to stress-test it with complex open-source licenses or API terms!
&lt;/h2&gt;

&lt;h2&gt;
  
  
  🛠️ Other Essential Tools in the Forge
&lt;/h2&gt;

&lt;p&gt;Aside from the AI helper, I’ve rolled out a suite of core text utilities that you can bookmark for your daily workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Case Converter: Instantly toggle text between camelCase, snake_case, PascalCase, UPPERCASE, and lowercase. Perfect for cleaning up variable lists.&lt;/li&gt;
&lt;li&gt;Find and Replace: Fast, browser-based regex and string swapping without opening an IDE.&lt;/li&gt;
&lt;li&gt;Word &amp;amp; Character Counter: Real-time analysis of your text length, reading time, and keyword density.&lt;/li&gt;
&lt;li&gt;Text Cleaners &amp;amp; Formatters: Strip unwanted whitespace, remove duplicate lines, and clean up messy data arrays instantly.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💡 Why Build This? (The Tech Stack)
&lt;/h2&gt;

&lt;p&gt;Most online text tools are bloated, slow, and tracking-heavy. I wanted to focus on speed and privacy.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The core text utilities run 100% client-side in your browser. Your data never touches a server.&lt;/li&gt;
&lt;li&gt;The layout is built to be clean, responsive, and distraction-free.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  👇 I Need Your Feedback!
&lt;/h2&gt;

&lt;p&gt;This is a living project, and I want to build what you actually use.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What text tool or developer utility do you find yourself needing most often?&lt;/li&gt;
&lt;li&gt;Are there any specific formatting edge cases my tools missed?&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Check out the full suite at FreeToolForge.org and let me know your thoughts, feature requests, or bugs in the comments below!
&lt;/h2&gt;

&lt;h2&gt;
  
  
  📈 Tips for Publishing This on Dev.to to Maximize Backlink Value:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use Exact URLs: When you post this, make sure to replace the general text-tools links above with the exact live URLs of those specific tools if they have their own dedicated sub-pages.&lt;/li&gt;
&lt;li&gt;Choose the Right Tags: Use tags like #devtools, #webdev, #opensource, and #ai. This pushes your post into the specific feeds of users looking for utilities.&lt;/li&gt;
&lt;li&gt;Engage in the Comments: When developers comment, reply immediately with a link to a specific tool that solves their problem. Dev.to comment sections pass great indexing signals to search engines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Are there any other specific text tools you have live on the site right now that we should add to this post list?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>nocode</category>
      <category>productivity</category>
      <category>devops</category>
    </item>
    <item>
      <title>10 free online tools every developer should bookmark in 2025</title>
      <dc:creator>Abid niazi</dc:creator>
      <pubDate>Sat, 30 May 2026 13:16:27 +0000</pubDate>
      <link>https://dev.to/abid_niazi_a7faa6246a7952/10-free-online-tools-every-developer-should-bookmark-in-2025-4364</link>
      <guid>https://dev.to/abid_niazi_a7faa6246a7952/10-free-online-tools-every-developer-should-bookmark-in-2025-4364</guid>
      <description>&lt;p&gt;Hi Muhammad abid,&lt;/p&gt;

&lt;p&gt;I've been following free tool forge  for a while and noticed you publish practical tooling guides for developers. I'd love to contribute a guest post titled:&lt;/p&gt;

&lt;p&gt;"10 free online tools every developer should bookmark in 2025"&lt;/p&gt;

&lt;p&gt;Here's a quick outline:&lt;/p&gt;

&lt;p&gt;— Introduction: why free tools close the gap between idea and execution&lt;br&gt;
— Section 1: Text &amp;amp; data tools (JSON formatters, regex testers, diff checkers)&lt;br&gt;
— Section 2: Code &amp;amp; API tools (curl converters, HTTP status checkers, Base64 encoders)&lt;br&gt;
— Section 3: Design &amp;amp; image tools (SVG optimisers, color pickers, favicon generators)&lt;br&gt;
— Section 4: Productivity &amp;amp; workflow tools&lt;br&gt;
— Section 5: Where to find more — including [&lt;a href="https://freetoolforge.org/" rel="noopener noreferrer"&gt;https://freetoolforge.org/&lt;/a&gt;], a directory of free browser tools with no signup required&lt;br&gt;
— Conclusion: building a personal toolbox&lt;/p&gt;

&lt;p&gt;The piece would be 100% original, around 1,200 words, with practical screenshots. I can include 1–2 contextual links to your own content as well.&lt;/p&gt;

&lt;p&gt;Would this be a good fit? Happy to adjust the angle to suit your audience.&lt;/p&gt;

&lt;p&gt;free tool forge &lt;br&gt;
&lt;a href="https://freetoolforge.org/" rel="noopener noreferrer"&gt;https://freetoolforge.org/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How Dual-URL Architecture Doubled My Indexed Pages (Next.js 14 SEO Strategy)</title>
      <dc:creator>Abid niazi</dc:creator>
      <pubDate>Thu, 21 May 2026 16:48:39 +0000</pubDate>
      <link>https://dev.to/abid_niazi_a7faa6246a7952/how-dual-url-architecture-doubled-my-indexed-pages-nextjs-14-seo-strategy-4fl1</link>
      <guid>https://dev.to/abid_niazi_a7faa6246a7952/how-dual-url-architecture-doubled-my-indexed-pages-nextjs-14-seo-strategy-4fl1</guid>
      <description>&lt;p&gt;After 4 months of building &lt;a href="https://freetoolforge.org" rel="noopener noreferrer"&gt;ToolForge&lt;/a&gt;, I have 152 organic keywords growing at +2,071%. Here's the single architectural decision that made the biggest difference.&lt;/p&gt;

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

&lt;p&gt;I built an online calculator. Let's say an EMI Calculator. It lives at:&lt;br&gt;
/tools/calculators/emi-calculator&lt;/p&gt;

&lt;p&gt;This page targets &lt;strong&gt;action keywords&lt;/strong&gt; — people who type "emi calculator" into Google. They want to USE the tool.&lt;/p&gt;

&lt;p&gt;But there's a second group: people who type "how to calculate emi in pakistan." They want to LEARN. They'll spend 5-10 minutes reading before they need a calculator.&lt;/p&gt;

&lt;p&gt;One URL can't serve both intents well. A tool page with a 3,000-word guide has terrible UX. A guide page with an embedded calculator is slow and unfocused.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Solution: Dual URLs Per Tool
&lt;/h2&gt;

&lt;p&gt;Every tool in ToolForge gets two pages:&lt;br&gt;
/tools/calculators/emi-calculator    ← Interactive tool&lt;br&gt;
/emi-calculator                       ← Long-form SEO guide&lt;/p&gt;

&lt;p&gt;In Next.js 14 App Router, this means:&lt;br&gt;
src/app/tools/calculators/emi-calculator/page.tsx   ← 'use client', interactive&lt;br&gt;
src/app/emi-calculator/page.tsx                      ← server component, 3000+ words&lt;/p&gt;

&lt;p&gt;The tool page is a client component with state, inputs, and instant results. The guide page is a server-rendered article with headings, tables, FAQs, and schema markup.&lt;/p&gt;
&lt;h2&gt;
  
  
  How It Works for SEO
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Tool page&lt;/strong&gt; (&lt;code&gt;/tools/calculators/emi-calculator&lt;/code&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Targets: "emi calculator", "loan calculator", "emi calculator pakistan"&lt;/li&gt;
&lt;li&gt;Schema: &lt;code&gt;SoftwareApplication&lt;/code&gt; JSON-LD&lt;/li&gt;
&lt;li&gt;Content: minimal — just the tool + brief instructions&lt;/li&gt;
&lt;li&gt;User intent: DO something&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Guide page&lt;/strong&gt; (&lt;code&gt;/emi-calculator&lt;/code&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Targets: "how to calculate emi", "emi formula", "bank loan rates pakistan"&lt;/li&gt;
&lt;li&gt;Schema: &lt;code&gt;FAQPage&lt;/code&gt; JSON-LD
&lt;/li&gt;
&lt;li&gt;Content: 3,000+ words — formula explanation, bank comparisons, tips, FAQs&lt;/li&gt;
&lt;li&gt;User intent: LEARN something&lt;/li&gt;
&lt;li&gt;Contains a prominent CTA button linking to the tool page&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The Results
&lt;/h2&gt;

&lt;p&gt;From 61 tools, this architecture generates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;61 tool pages (action keywords)&lt;/li&gt;
&lt;li&gt;61 guide pages (informational keywords)
&lt;/li&gt;
&lt;li&gt;= 122 indexable pages from the same number of tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our sitemap has 100+ URLs. Google has indexed 86 so far, and we're ranking for 152 keywords.&lt;/p&gt;

&lt;p&gt;The guide pages consistently rank higher than tool pages because Google favors comprehensive content for informational queries. But the tool pages capture direct-intent traffic from people who just want a calculator.&lt;/p&gt;
&lt;h2&gt;
  
  
  Implementation Pattern
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Tool page&lt;/strong&gt; (client component):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app/tools/calculators/emi-calculator/page.tsx&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;RelatedTools&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/components/ui/RelatedTools&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;EMICalculator&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setAmount&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setRate&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// ... calculator logic&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"max-w-4xl mx-auto p-6"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;EMI Calculator&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Interactive inputs + results */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RelatedTools&lt;/span&gt; &lt;span class="na"&gt;recommendedIds&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;business-loan-calculator&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;fbr-tax-calculator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Guide page&lt;/strong&gt; (server component):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app/emi-calculator/page.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;EMIGuide&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="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"max-w-4xl mx-auto p-6"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;How to Calculate EMI in Pakistan 2026&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* CTA to the actual tool */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/tools/calculators/emi-calculator"&lt;/span&gt;
        &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"bg-indigo-600 px-6 py-3 rounded-xl text-white"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Calculate Your EMI Now →
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* 3,000+ words of guide content */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;What is EMI?&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;EMI stands for Equated Monthly Installment...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* FAQs with schema markup */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Frequently Asked Questions&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* ... */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Layout&lt;/strong&gt; (metadata for each):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app/tools/calculators/emi-calculator/layout.tsx&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EMI Calculator — Free Loan EMI Calculator Pakistan&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Calculate your monthly EMI instantly...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;alternates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;canonical&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://freetoolforge.org/tools/calculators/emi-calculator&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="c1"&gt;// src/app/emi-calculator/layout.tsx  &lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;How to Calculate EMI in Pakistan 2026 — Complete Guide&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Learn how EMI is calculated with formula...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;alternates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;canonical&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://freetoolforge.org/emi-calculator&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Different canonical URLs, different titles, different meta descriptions. Google treats them as separate pages targeting different keywords.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cross-Linking Strategy
&lt;/h2&gt;

&lt;p&gt;The guide page links to the tool: "Calculate your EMI instantly →"&lt;br&gt;
The tool page links to the guide via a &lt;code&gt;DocButton&lt;/code&gt; component: "Read the full guide →"&lt;/p&gt;

&lt;p&gt;This creates a bidirectional internal link loop that passes authority between both pages. When the guide ranks well, it boosts the tool. When the tool ranks well, it boosts the guide.&lt;/p&gt;

&lt;h2&gt;
  
  
  When NOT to Use Dual URLs
&lt;/h2&gt;

&lt;p&gt;Not every tool needs a guide. If the tool is self-explanatory (like a UUID Generator), the guide page would be thin content — which hurts SEO.&lt;/p&gt;

&lt;p&gt;Use dual URLs when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The topic has informational search intent ("how to...")&lt;/li&gt;
&lt;li&gt;You can write 1,500+ words of genuine, useful content&lt;/li&gt;
&lt;li&gt;The tool targets a different keyword than the guide&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Skip the guide when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The tool is self-explanatory (Base64 encoder, color picker)&lt;/li&gt;
&lt;li&gt;You'd be padding content just to fill the page&lt;/li&gt;
&lt;li&gt;The keyword volume for "how to" queries is under 100/month&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The entire architecture is live at &lt;a href="https://freetoolforge.org" rel="noopener noreferrer"&gt;freetoolforge.org&lt;/a&gt;. You can see the pattern in action:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tool: &lt;a href="https://freetoolforge.org/tools/calculators/nust-merit-calculator" rel="noopener noreferrer"&gt;NUST Merit Calculator&lt;/a&gt; → Guide: &lt;a href="https://freetoolforge.org/nust-merit-calculator" rel="noopener noreferrer"&gt;How to Calculate NUST Aggregate&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Tool: &lt;a href="https://freetoolforge.org/tools/text-tools/ai-essay-writer" rel="noopener noreferrer"&gt;AI Essay Writer&lt;/a&gt; → Guide: &lt;a href="https://freetoolforge.org/ai-essay-writer" rel="noopener noreferrer"&gt;AI Essay Writer Guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Tool: &lt;a href="https://freetoolforge.org/tools/calculators/fbr-tax-calculator" rel="noopener noreferrer"&gt;FBR Tax Calculator&lt;/a&gt; → Guide: &lt;a href="https://freetoolforge.org/fbr-tax-calculator" rel="noopener noreferrer"&gt;FBR Tax Guide&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;61 tools × 2 URLs = 122 pages. The dual-URL architecture is the single biggest SEO lever we've pulled.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/abidniazi" rel="noopener noreferrer"&gt;Abid Niazi&lt;/a&gt; — Full Stack Developer, Pakistan&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="https://freetoolforge.org" rel="noopener noreferrer"&gt;freetoolforge.org&lt;/a&gt; — 61 free tools, zero ads&lt;/em&gt;&lt;a href="https://dev.tourl"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How I Handle 150K Free API Calls/Month with a 5-Key Rotation System in Next.js</title>
      <dc:creator>Abid niazi</dc:creator>
      <pubDate>Thu, 21 May 2026 16:46:32 +0000</pubDate>
      <link>https://dev.to/abid_niazi_a7faa6246a7952/how-i-handle-150k-free-api-callsmonth-with-a-5-key-rotation-system-in-nextjs-3cda</link>
      <guid>https://dev.to/abid_niazi_a7faa6246a7952/how-i-handle-150k-free-api-callsmonth-with-a-5-key-rotation-system-in-nextjs-3cda</guid>
      <description>&lt;p&gt;Most free-tier APIs give you 15 requests/minute. That's fine for a side project — until your tools start getting real traffic.&lt;/p&gt;

&lt;p&gt;I built &lt;a href="https://freetoolforge.org" rel="noopener noreferrer"&gt;ToolForge&lt;/a&gt;, a free toolkit with 61 tools including 4 AI-powered features (summarizer, grammar checker, paraphraser, essay writer). All powered by Google Gemini's free tier.&lt;/p&gt;

&lt;p&gt;The challenge: 15 req/min × 1 key = dead tool after 15 users.&lt;/p&gt;

&lt;p&gt;The solution: rotate across 5 API keys with automatic failover.&lt;/p&gt;

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

&lt;p&gt;Gemini's free tier is generous — 15 requests per minute, 1,500 per day per key. But a single key means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User #16 in any given minute gets a 429 error&lt;/li&gt;
&lt;li&gt;Your "free AI tool" stops working during peak hours&lt;/li&gt;
&lt;li&gt;Users lose trust and never come back&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I needed a system that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Distributes requests across multiple keys&lt;/li&gt;
&lt;li&gt;Automatically skips rate-limited keys&lt;/li&gt;
&lt;li&gt;Never shows a 429 to the end user&lt;/li&gt;
&lt;li&gt;Works without any external state management&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Solution: Round-Robin with Failover
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/lib/gemini-keys.ts (simplified)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;API_KEYS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GEMINI_API_KEY_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GEMINI_API_KEY_2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GEMINI_API_KEY_3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GEMINI_API_KEY_4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GEMINI_API_KEY_5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;currentKeyIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchWithKeyRotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gemini-2.5-flash-lite&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;maxAttempts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;API_KEYS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;maxAttempts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;API_KEYS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;currentKeyIndex&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;try&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;response&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`https://generativelanguage.googleapis.com/v1beta/models/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:generateContent`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&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;Content-Type&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;application/json&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-goog-api-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
          &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="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="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Rate limited — rotate to next key&lt;/span&gt;
        &lt;span class="nx"&gt;currentKeyIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentKeyIndex&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;API_KEYS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;continue&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`API error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="c1"&gt;// Success — rotate for next request (spread load)&lt;/span&gt;
      &lt;span class="nx"&gt;currentKeyIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentKeyIndex&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;API_KEYS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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;data&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;||&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;catch &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="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;currentKeyIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentKeyIndex&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;API_KEYS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;maxAttempts&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&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="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;All API keys exhausted&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Round-robin by default&lt;/strong&gt;: Each request uses the next key in sequence. This spreads load evenly — key 1 handles request 1, key 2 handles request 2, etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Automatic failover on 429&lt;/strong&gt;: If a key is rate-limited, the loop immediately tries the next key. No delay, no retry timer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Wraps around&lt;/strong&gt;: After key 5, it goes back to key 1. The modulo operator handles this.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No external state&lt;/strong&gt;: &lt;code&gt;currentKeyIndex&lt;/code&gt; lives in server memory. For a single Vercel serverless function, this works perfectly. For multi-instance deployments, you'd want Redis — but for our scale, this is enough.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Math
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Keys&lt;/th&gt;
&lt;th&gt;Requests/Min&lt;/th&gt;
&lt;th&gt;Requests/Day&lt;/th&gt;
&lt;th&gt;Requests/Month&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 key&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;1,500&lt;/td&gt;
&lt;td&gt;45,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3 keys&lt;/td&gt;
&lt;td&gt;45&lt;/td&gt;
&lt;td&gt;4,500&lt;/td&gt;
&lt;td&gt;135,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;5 keys&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;75&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7,500&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;225,000&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;With 5 keys, we handle 75 simultaneous requests per minute and 225,000 per month — all on the free tier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using It in API Routes
&lt;/h2&gt;

&lt;p&gt;Every AI tool calls the same function:&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;// src/app/api/summarize/route.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;fetchWithKeyRotation&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/lib/gemini-keys&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&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;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Summarize the following text in 3-5 sentences:\n\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;summary&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;fetchWithKeyRotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prompt&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;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;summary&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;The AI Summarizer, Grammar Checker, Paraphraser, and Essay Writer all use the exact same &lt;code&gt;fetchWithKeyRotation()&lt;/code&gt; function. Adding a new AI tool takes 10 minutes — just create a new API route with a different prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rate Limiting Users (Not Just Keys)
&lt;/h2&gt;

&lt;p&gt;Keys handle API-side limits. But we also need to prevent individual users from draining all keys:&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;// Simple IP-based rate limit&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rateLimitMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;number&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;function&lt;/span&gt; &lt;span class="nf"&gt;isRateLimited&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxRequests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;windowMs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60000&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;now&lt;/span&gt; &lt;span class="o"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timestamps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rateLimitMap&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="nx"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&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;recent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;timestamps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;windowMs&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="nx"&gt;recent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;maxRequests&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;recent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;rateLimitMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;recent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives each user 5 requests per minute — enough for genuine use, not enough to abuse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Free tiers are viable at scale&lt;/strong&gt; if you architect around their limits. Don't default to paid APIs until you've exhausted creative solutions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Round-robin beats random selection&lt;/strong&gt;. Random can hit the same key twice; round-robin guarantees even distribution.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Always rotate after success too&lt;/strong&gt; — not just on failure. This prevents one key from handling 90% of traffic while others sit idle.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Monitor key usage&lt;/strong&gt;. We log which key handled each request. This tells us if one key is consistently rate-limited (meaning traffic exceeded capacity).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;All 4 AI tools are live and free at &lt;a href="https://freetoolforge.org" rel="noopener noreferrer"&gt;freetoolforge.org&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://freetoolforge.org/tools/text-tools/ai-summarizer" rel="noopener noreferrer"&gt;AI Text Summarizer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://freetoolforge.org/tools/text-tools/ai-grammar-checker" rel="noopener noreferrer"&gt;AI Grammar Checker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://freetoolforge.org/tools/text-tools/ai-paraphraser" rel="noopener noreferrer"&gt;AI Paraphraser&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://freetoolforge.org/tools/text-tools/ai-essay-writer" rel="noopener noreferrer"&gt;AI Essay Writer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each tool handles hundreds of daily users without a single paid API call.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built by &lt;a href="https://github.com/abidniazi" rel="noopener noreferrer"&gt;Abid Niazi&lt;/a&gt; — Full Stack Developer, Pakistan&lt;/em&gt;&lt;br&gt;
&lt;em&gt;ToolForge: &lt;a href="https://freetoolforge.org" rel="noopener noreferrer"&gt;freetoolforge.org&lt;/a&gt; — 61 free tools, no ads, no signup&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I Built 56 Free Tools for Pakistani Students Using Next.js 14 — Architecture Deep Dive</title>
      <dc:creator>Abid niazi</dc:creator>
      <pubDate>Mon, 18 May 2026 22:52:16 +0000</pubDate>
      <link>https://dev.to/abid_niazi_a7faa6246a7952/i-built-56-free-tools-for-pakistani-students-using-nextjs-14-architecture-deep-dive-50he</link>
      <guid>https://dev.to/abid_niazi_a7faa6246a7952/i-built-56-free-tools-for-pakistani-students-using-nextjs-14-architecture-deep-dive-50he</guid>
      <description>&lt;p&gt;Six months ago, I started building &lt;a href="https://freetoolforge.org" rel="noopener noreferrer"&gt;ToolForge&lt;/a&gt; — a free online toolkit with 56+ tools for students, developers, and professionals.&lt;/p&gt;

&lt;p&gt;No ads. No signup. No data collection. Everything processes in the browser.&lt;/p&gt;

&lt;p&gt;Here's the technical deep dive into how it's built.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js 14 App Router&lt;/strong&gt; — server components for SEO, client components for interactivity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript&lt;/strong&gt; — caught hundreds of bugs before deployment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tailwind CSS&lt;/strong&gt; — consistent dark theme across 56+ pages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini AI&lt;/strong&gt; — powers 4 AI tools via a custom key rotation system&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vercel&lt;/strong&gt; — auto-deploy on push, free tier handles all traffic&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Dual-URL Architecture
&lt;/h2&gt;

&lt;p&gt;Every tool gets TWO indexable URLs:&lt;br&gt;
/tools/calculators/emi-calculator    ← the interactive tool&lt;br&gt;
/emi-calculator                       ← long-form SEO guide page&lt;/p&gt;

&lt;p&gt;The tool page targets action keywords ("emi calculator"). The guide page targets informational keywords ("how to calculate emi in pakistan"). This doubled our indexed pages without duplicating content.&lt;/p&gt;
&lt;h2&gt;
  
  
  The AI Challenge: Free Tier Key Rotation
&lt;/h2&gt;

&lt;p&gt;We built 4 AI tools (Summarizer, Grammar Checker, Paraphraser, Essay Writer) using Google Gemini's free tier.&lt;/p&gt;

&lt;p&gt;The problem? 15 requests/minute per key.&lt;/p&gt;

&lt;p&gt;Our solution: round-robin rotation across 5 API keys with automatic failover on 429 errors.&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;// Simplified version of our key rotation&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;KEY_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;KEY_2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;KEY_3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;KEY_4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;KEY_5&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;currentIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&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;fetchWithKeyRotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;attempts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;attempts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;attempts&lt;/span&gt;&lt;span class="o"&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;try&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;response&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GEMINI_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&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-goog-api-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;currentIndex&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;currentIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentIndex&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;continue&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&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;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;currentIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentIndex&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;All keys exhausted&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result: 150,000 free API calls/month. Our AI tools are genuinely free to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pakistan-Specific Tools
&lt;/h2&gt;

&lt;p&gt;Instead of competing globally, we built tools nobody else has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MDCAT Aggregate Calculator&lt;/strong&gt; — medical college admission formula&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NUST/FAST/UET Merit Calculators&lt;/strong&gt; — university admission aggregates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FBR Income Tax Calculator&lt;/strong&gt; — Pakistan tax slabs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Electricity Bill Calculator&lt;/strong&gt; — NEPRA slab rates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zakat Calculator&lt;/strong&gt; — Islamic finance with PKR nisab values&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;University Comparator&lt;/strong&gt; — compare aggregate across 5 universities at once&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These tools have almost zero global competition but massive local demand.&lt;/p&gt;

&lt;h2&gt;
  
  
  SEO Results (3 Months)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Indexed pages&lt;/td&gt;
&lt;td&gt;86&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Organic keywords&lt;/td&gt;
&lt;td&gt;82 (+1,071%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CTR&lt;/td&gt;
&lt;td&gt;12.1% (industry avg: 2-5%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total tools&lt;/td&gt;
&lt;td&gt;56&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Guides/blogs&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The high CTR tells us titles work. The average position (~23) tells us we need more backlinks. Hence this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Blogs drive more traffic than tools.&lt;/strong&gt; Our pillar guides (25-30 min reads) rank better than individual tool pages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build for a niche first.&lt;/strong&gt; Pakistan-specific tools have zero competition. We rank for keywords no one else targets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key rotation makes free tiers viable.&lt;/strong&gt; Don't pay for APIs until you've exhausted free tier creatively.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dual URLs per tool = 2x indexed pages.&lt;/strong&gt; Simple architectural decision with massive SEO impact.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Everything is live and free at &lt;a href="https://freetoolforge.org" rel="noopener noreferrer"&gt;freetoolforge.org&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://freetoolforge.org/tools/text-tools/ai-essay-writer" rel="noopener noreferrer"&gt;AI Essay Writer&lt;/a&gt; and &lt;a href="https://freetoolforge.org/tools/calculators/university-compare" rel="noopener noreferrer"&gt;University Comparator&lt;/a&gt; are the most popular tools right now.&lt;/p&gt;

&lt;p&gt;Happy to answer questions about the architecture in the comments.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built by &lt;a href="https://github.com/abidniazi" rel="noopener noreferrer"&gt;Abid Niazi&lt;/a&gt; — Full Stack Developer, Pakistan&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>I Built 56 Free Tools for Pakistani Students Using Next.js — Here's What I Learned</title>
      <dc:creator>Abid niazi</dc:creator>
      <pubDate>Fri, 15 May 2026 11:43:35 +0000</pubDate>
      <link>https://dev.to/abid_niazi_a7faa6246a7952/i-built-56-free-tools-for-pakistani-students-using-nextjs-heres-what-i-learned-5glp</link>
      <guid>https://dev.to/abid_niazi_a7faa6246a7952/i-built-56-free-tools-for-pakistani-students-using-nextjs-heres-what-i-learned-5glp</guid>
      <description>

&lt;p&gt;Six months ago, I started building &lt;a href="https://freetoolforge.org" rel="noopener noreferrer"&gt;ToolForge&lt;/a&gt; — a completely free online toolkit for students, developers, and professionals in Pakistan.&lt;/p&gt;

&lt;p&gt;Today it has &lt;strong&gt;56 tools&lt;/strong&gt;, 86 indexed pages on Google, and gets organic traffic from 15+ countries. No ads, no signup, no data collection.&lt;/p&gt;

&lt;p&gt;Here's the technical journey — what worked, what broke, and what I'd do differently.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js 14 App Router&lt;/strong&gt; — SSR for SEO, server components for speed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript&lt;/strong&gt; — caught 200+ bugs before deployment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tailwind CSS&lt;/strong&gt; — shipped UI 3x faster than custom CSS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vercel&lt;/strong&gt; — auto-deploy on git push, free tier handles our traffic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini API&lt;/strong&gt; — powers 4 AI tools (summarizer, grammar checker, paraphraser, essay writer)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture Decision: Why App Router?
&lt;/h2&gt;

&lt;p&gt;Every tool page needed to be independently indexable by Google. The App Router's file-based routing made this natural:&lt;br&gt;
src/app/tools/calculators/emi-calculator/page.tsx    ← tool&lt;br&gt;
src/app/emi-calculator/page.tsx                       ← SEO guide&lt;/p&gt;

&lt;p&gt;Each tool gets TWO URLs — the tool itself and a long-form guide page targeting different keywords. This doubled our indexable pages without doubling content.&lt;/p&gt;

&lt;h2&gt;
  
  
  The AI Tools Challenge
&lt;/h2&gt;

&lt;p&gt;We built 4 AI-powered tools using Google's Gemini API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI Text Summarizer&lt;/li&gt;
&lt;li&gt;AI Grammar Checker
&lt;/li&gt;
&lt;li&gt;AI Paraphraser&lt;/li&gt;
&lt;li&gt;AI Essay Writer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The challenge? Rate limits. Gemini's free tier gives 15 requests/minute per key.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution: Key rotation system&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We rotate across 5 API keys with automatic failover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Round-robin selection&lt;/li&gt;
&lt;li&gt;Auto-switch on 429 (rate limit) errors
&lt;/li&gt;
&lt;li&gt;Capacity: 150,000 requests/month on free tier&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means our AI tools are genuinely free — no hidden costs passed to users.&lt;/p&gt;

&lt;h2&gt;
  
  
  SEO Results (Real Numbers)
&lt;/h2&gt;

&lt;p&gt;After 3 months:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;86 pages indexed by Google&lt;/li&gt;
&lt;li&gt;1,750 impressions, 244 clicks&lt;/li&gt;
&lt;li&gt;13.9% CTR (industry average is 2-5%)&lt;/li&gt;
&lt;li&gt;Average position: 20.9&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The high CTR tells me our titles and meta descriptions are working. The average position (page 2-3) tells me we need more backlinks and content depth to break into page 1.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pakistan-Specific Tools (Our Niche)
&lt;/h2&gt;

&lt;p&gt;Instead of competing with global tools, we built Pakistan-specific calculators:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MDCAT Aggregate Calculator (medical college admissions)&lt;/li&gt;
&lt;li&gt;NUST/FAST/UET Merit Calculators (university admissions)&lt;/li&gt;
&lt;li&gt;FBR Income Tax Calculator&lt;/li&gt;
&lt;li&gt;Freelancer Tax Calculator (PSEB exemptions)&lt;/li&gt;
&lt;li&gt;Electricity Bill Calculator (NEPRA slab rates)&lt;/li&gt;
&lt;li&gt;Zakat Calculator&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These have almost zero competition internationally but massive local demand.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start with blogs, not tools&lt;/strong&gt; — Our pillar blog posts drive more traffic than individual tools&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build the sitemap correctly from day 1&lt;/strong&gt; — We had indexing issues from a misconfigured redirect&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus on 10 tools, not 56&lt;/strong&gt; — Better to have 10 perfect tools than 56 good ones&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Get backlinks early&lt;/strong&gt; — DA matters more than I thought for ranking speed&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;The entire site is live at &lt;a href="https://freetoolforge.org" rel="noopener noreferrer"&gt;freetoolforge.org&lt;/a&gt;. Everything is free, no signup required.&lt;/p&gt;

&lt;p&gt;If you're building a tools site, I'm happy to share more details about the architecture in the comments.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fohqlwt4mhce1s24io78r.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fohqlwt4mhce1s24io78r.png" alt=" " width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>Building Privacy-First Browser Tools: How ToolForge Processes PDFs Without a Server</title>
      <dc:creator>Abid niazi</dc:creator>
      <pubDate>Thu, 14 May 2026 06:54:30 +0000</pubDate>
      <link>https://dev.to/abid_niazi_a7faa6246a7952/building-privacy-first-browser-tools-how-toolforge-processes-pdfs-without-a-server-1m3j</link>
      <guid>https://dev.to/abid_niazi_a7faa6246a7952/building-privacy-first-browser-tools-how-toolforge-processes-pdfs-without-a-server-1m3j</guid>
      <description>&lt;p&gt;Most "free" online PDF tools upload your files to their servers.&lt;br&gt;
That means your medical documents, salary slips, financial records,&lt;br&gt;
and confidential contracts are sitting on someone else's computer.&lt;/p&gt;

&lt;p&gt;When I built ToolForge's 12 PDF tools, I made a hard decision: &lt;strong&gt;nothing&lt;br&gt;
leaves the browser.&lt;/strong&gt; Here's exactly how I built it and what I learned.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why Browser-Only Matters
&lt;/h2&gt;

&lt;p&gt;When you compress a PDF on most free tools, here's what actually happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your file uploads to their AWS/Google Cloud bucket&lt;/li&gt;
&lt;li&gt;Server-side code processes it&lt;/li&gt;
&lt;li&gt;Processed file downloads back to you&lt;/li&gt;
&lt;li&gt;Your original file sits in their storage (usually deleted after 24h... usually)
For a salary slip, medical report, or legal document — this is a real
privacy risk. Browser-only processing eliminates it entirely.&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  The Core Library: PDF.js + PDF-lib
&lt;/h2&gt;

&lt;p&gt;I use two libraries for different PDF operations:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PDF.js&lt;/strong&gt; (Mozilla) — for reading and rendering PDFs&lt;br&gt;
&lt;strong&gt;PDF-lib&lt;/strong&gt; — for creating, modifying, and manipulating PDFs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;pdfjs-dist pdf-lib
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  PDF Compression (Client-Side)
&lt;/h2&gt;

&lt;p&gt;True compression requires re-encoding, which is complex client-side.&lt;br&gt;
My approach: re-render each PDF page to a canvas at slightly reduced&lt;br&gt;
quality, then rebuild the PDF from those images.&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;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;pdfjsLib&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pdfjs-dist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PDFDocument&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pdf-lib&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;compressPDF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;quality&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.7&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;arrayBuffer&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;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&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;pdf&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;pdfjsLib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;promise&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;newPdf&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;PDFDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;numPages&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&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;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&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;viewport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getViewport&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Render page to canvas&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;canvas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&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;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;canvasContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;viewport&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Convert canvas to JPEG (this is where compression happens)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageDataUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toDataURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/jpeg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;quality&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;imageBytes&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageDataUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="c1"&gt;// Add to new PDF&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jpgImage&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;newPdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;embedJpg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageBytes&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;newPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newPdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPage&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="nx"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jpgImage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;newPdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This achieves 40–70% file size reduction while keeping all pages intact.&lt;br&gt;
Everything runs in the user's browser — no upload required.&lt;/p&gt;


&lt;h2&gt;
  
  
  PDF Merging
&lt;/h2&gt;

&lt;p&gt;Merging is simpler — PDF-lib handles this natively:&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;mergePDFs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;files&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;mergedPdf&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;PDFDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;for &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;file&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;files&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;arrayBuffer&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;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&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;pdf&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;PDFDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arrayBuffer&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;pages&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;mergedPdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copyPages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPageIndices&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nx"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;mergedPdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mergedPdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&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;Drop it in a &lt;code&gt;&amp;lt;input type="file" multiple accept=".pdf"&amp;gt;&lt;/code&gt;, call this&lt;br&gt;
function, and trigger a download. That's the entire merge tool.&lt;/p&gt;


&lt;h2&gt;
  
  
  PDF Rotation
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;degrees&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pdf-lib&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;rotatePDF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rotation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;90&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;arrayBuffer&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;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&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;pdf&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;PDFDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arrayBuffer&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;pages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPages&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&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;currentRotation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRotation&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setRotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;degrees&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentRotation&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;rotation&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&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;
  
  
  Password Protection (Lock/Unlock PDF)
&lt;/h2&gt;

&lt;p&gt;PDF-lib supports encryption. For locking:&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;lockPDF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&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;arrayBuffer&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;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&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;pdf&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;PDFDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// PDF-lib uses the standard PDF encryption spec&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pdfBytes&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;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;userPassword&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;ownerPassword&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_owner&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;printing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lowResolution&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;modifying&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;copying&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;pdfBytes&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;For unlocking — you load the PDF with the user-provided password:&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;unlockPDF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&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;arrayBuffer&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;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&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;pdf&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;PDFDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Save without password&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&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;
  
  
  Handling Large Files
&lt;/h2&gt;

&lt;p&gt;The main challenge with browser-based PDF processing is memory.&lt;br&gt;
Large files (50MB+) can crash the browser tab. My solution:&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="c1"&gt;// Check file size before processing&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MAX_SIZE_MB&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&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="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;MAX_SIZE_MB&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`File too large. Maximum size is &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;MAX_SIZE_MB&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;MB.`&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="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Process in chunks for multi-page documents&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CHUNK_SIZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// pages per chunk&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;totalPages&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;CHUNK_SIZE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;processPageChunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;CHUNK_SIZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;totalPages&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="c1"&gt;// Small delay to prevent UI freeze&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&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;
  
  
  The Result: 12 PDF Tools, Zero Server Uploads
&lt;/h2&gt;

&lt;p&gt;ToolForge now has 12 browser-based PDF tools:&lt;br&gt;
Compress, Merge, Split, Rotate, Lock, Unlock, Watermark,&lt;br&gt;
Sign, PDF↔Word, PDF→JPG, PDF→Excel, Image→PDF.&lt;/p&gt;

&lt;p&gt;Every single one processes files locally. No data ever leaves your browser.&lt;/p&gt;

&lt;p&gt;The full toolkit is free at &lt;a href="https://freetoolforge.org/tools/document-tools" rel="noopener noreferrer"&gt;freetoolforge.org/tools/document-tools&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have you built browser-based file processing tools? What was your biggest&lt;br&gt;
challenge? Drop it in the comments — I'd genuinely like to know.&lt;/p&gt;




&lt;p&gt;WORD COUNT: ~780 words&lt;br&gt;
ESTIMATED READ TIME: 5 min&lt;/p&gt;







</description>
      <category>javascript</category>
      <category>ai</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How I Built a Free CGPA, MDCAT &amp; Merit Calculator for Pakistani Students</title>
      <dc:creator>Abid niazi</dc:creator>
      <pubDate>Thu, 14 May 2026 06:52:09 +0000</pubDate>
      <link>https://dev.to/abid_niazi_a7faa6246a7952/how-i-built-a-free-cgpa-mdcat-merit-calculator-for-pakistani-students-41kd</link>
      <guid>https://dev.to/abid_niazi_a7faa6246a7952/how-i-built-a-free-cgpa-mdcat-merit-calculator-for-pakistani-students-41kd</guid>
      <description>&lt;p&gt;Pakistani students get ignored by most tools on the internet.&lt;/p&gt;

&lt;p&gt;Every CGPA calculator uses the US 4.0 grading scale. Every merit calculator&lt;br&gt;
doesn't know what NUST or FAST or MDCAT is. Every EMI calculator shows&lt;br&gt;
interest rates in USD.&lt;/p&gt;

&lt;p&gt;So I built ToolForge — a free toolkit specifically for Pakistan. Here's what&lt;br&gt;
I learned building three of its most-used calculators.&lt;/p&gt;


&lt;h2&gt;
  
  
  The CGPA Calculator Problem
&lt;/h2&gt;

&lt;p&gt;Pakistan's Higher Education Commission (HEC) uses a grading scale that's&lt;br&gt;
subtly different from the US system most calculators are built on.&lt;/p&gt;

&lt;p&gt;The key difference: in Pakistan, both A and A+ map to 4.0 grade points.&lt;br&gt;
In the US, A+ is sometimes 4.3. This sounds minor but it causes significant&lt;br&gt;
errors when students use US-based calculators for Pakistani universities.&lt;/p&gt;

&lt;p&gt;Here's the HEC scale:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A+ = 4.0  (90–100%)
A  = 4.0  (85–89%)
A- = 3.7  (80–84%)
B+ = 3.3  (75–79%)
B  = 3.0  (70–74%)
B- = 2.7  (65–69%)
C+ = 2.3  (60–64%)
C  = 2.0  (55–59%)
F  = 0.0  (below 50%)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CGPA formula itself is straightforward:&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;calculateCGPA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subjects&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;totalWeightedPoints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;subjects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gradePoints&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;creditHours&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;0&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;totalCreditHours&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;subjects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;creditHours&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;0&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="nx"&gt;totalWeightedPoints&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;totalCreditHours&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&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;The tricky part was building the UI to dynamically add/remove subjects&lt;br&gt;
while keeping the calculation reactive. I used React state with an array&lt;br&gt;
of subject objects:&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;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;subjects&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSubjects&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;grade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;creditHours&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&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;Try the live calculator: &lt;a href="https://freetoolforge.org/tools/calculators/gpa-calculator" rel="noopener noreferrer"&gt;freetoolforge.org/tools/calculators/gpa-calculator&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The MDCAT Aggregate Calculator
&lt;/h2&gt;

&lt;p&gt;MDCAT (Medical and Dental College Admission Test) uses a specific formula&lt;br&gt;
that most calculators get wrong:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MDCAT Merit = (Matric % × 10%) + (FSc % × 40%) + (MDCAT Score × 50%)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Different universities then apply their own cutoffs on top of this aggregate.&lt;br&gt;
I built a version that calculates the standard PMC formula and shows you&lt;br&gt;
which universities you're likely eligible for based on last year's cutoffs.&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;calculateMDCATAggregate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;matric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fsc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mdcatScore&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;matricWeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;matric&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&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;fscWeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fsc&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;40&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;mdcatWeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mdcatScore&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;50&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="nx"&gt;matricWeight&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;fscWeight&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;mdcatWeight&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&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;Live tool: &lt;a href="https://freetoolforge.org/tools/calculators/mdcat-aggregate-calculator" rel="noopener noreferrer"&gt;freetoolforge.org/tools/calculators/mdcat-aggregate-calculator&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Merit Calculator Challenge
&lt;/h2&gt;

&lt;p&gt;The Merit Calculator was the hardest to build — not because of the math,&lt;br&gt;
but because every Pakistani university has a different formula:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NUST: Entry test 75% + FSc 15% + Matric 10%&lt;/li&gt;
&lt;li&gt;FAST: Their own aptitude test with its own weighting&lt;/li&gt;
&lt;li&gt;UET Lahore: Matric + FSc + UET entry test&lt;/li&gt;
&lt;li&gt;LUMS: SAT-based, entirely different system
I ended up building it with a dropdown to select the university, then
dynamically rendering the correct formula and input fields.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key technical decision: I stored all university formulas in a config&lt;br&gt;
object rather than hardcoding them, so adding a new university is just&lt;br&gt;
adding an object entry:&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;const&lt;/span&gt; &lt;span class="nx"&gt;UNIVERSITY_FORMULAS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;NUST&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NUST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;formula&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;matric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fsc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;entryTest&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="nx"&gt;matric&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fsc&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entryTest&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.75&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&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;Matric %&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;FSc %&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;NET Score (out of 200)&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="na"&gt;UET&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UET Lahore&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;formula&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;matric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fsc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;entryTest&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="nx"&gt;matric&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fsc&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.40&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entryTest&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&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;Matric %&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;FSc %&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;UET Entry Test %&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="c1"&gt;// ... more universities&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Live tool: &lt;a href="https://freetoolforge.org/tools/calculators/merit-calculator" rel="noopener noreferrer"&gt;freetoolforge.org/tools/calculators/merit-calculator&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned Building Pakistan-Specific Tools
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Context is everything.&lt;/strong&gt;&lt;br&gt;
A generic calculator is useless to a Pakistani student. Pakistan has its own&lt;br&gt;
grading scales, tax slabs, bank rates, and admission formulas. Building&lt;br&gt;
for a specific context — even a smaller audience — gives you users who&lt;br&gt;
are deeply engaged because the tool actually works for their situation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Privacy matters more than people admit.&lt;/strong&gt;&lt;br&gt;
All ToolForge calculations happen in the browser. No data is sent to any&lt;br&gt;
server. Users don't have to worry about their salary, CGPA, or tax info&lt;br&gt;
being stored anywhere. This was a deliberate design choice and users&lt;br&gt;
genuinely appreciate it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. SEO for specific audiences is easier.&lt;/strong&gt;&lt;br&gt;
"CGPA calculator" is a competitive keyword globally. "CGPA calculator&lt;br&gt;
Pakistan HEC scale" is almost entirely uncontested. Building for a specific&lt;br&gt;
audience means you rank faster with less effort.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js 14&lt;/strong&gt; App Router with TypeScript&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tailwind CSS&lt;/strong&gt; for all styling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vercel&lt;/strong&gt; for deployment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Gemini API&lt;/strong&gt; for AI-powered tools (with key rotation)&lt;/li&gt;
&lt;li&gt;All calculations run client-side — zero database
The full toolkit is live at &lt;a href="https://freetoolforge.org" rel="noopener noreferrer"&gt;freetoolforge.org&lt;/a&gt; —
50+ free tools, no signup, no ads. I'd love feedback from Pakistani
developers and students on what to build next.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What country-specific tools have you built or wished existed?&lt;/p&gt;




&lt;p&gt;WORD COUNT: ~820 words&lt;br&gt;
ESTIMATED READ TIME: 5 min&lt;/p&gt;







</description>
      <category>ai</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Ultimate Free Productivity Toolkit for Students in 2026</title>
      <dc:creator>Abid niazi</dc:creator>
      <pubDate>Fri, 08 May 2026 18:02:27 +0000</pubDate>
      <link>https://dev.to/abid_niazi_a7faa6246a7952/the-ultimate-free-productivity-toolkit-for-students-in-2026-1dgd</link>
      <guid>https://dev.to/abid_niazi_a7faa6246a7952/the-ultimate-free-productivity-toolkit-for-students-in-2026-1dgd</guid>
      <description>&lt;p&gt;University students today handle much more than classes.&lt;/p&gt;

&lt;p&gt;Assignments.&lt;br&gt;
Presentations.&lt;br&gt;
PDF submissions.&lt;br&gt;
Internships.&lt;br&gt;
Freelancing.&lt;br&gt;
Online learning.&lt;br&gt;
CV building.&lt;br&gt;
Research work.&lt;/p&gt;

&lt;p&gt;The problem is that students constantly switch between dozens of websites just to complete small tasks.&lt;/p&gt;

&lt;p&gt;One tool for GPA calculation.&lt;br&gt;
Another for PDF compression.&lt;br&gt;
Another for grammar correction.&lt;br&gt;
Another for word counting.&lt;/p&gt;

&lt;p&gt;Recently, I explored a browser-based platform called ToolForge, and it surprisingly combines many useful student productivity tools in one place.&lt;/p&gt;

&lt;p&gt;Here are the tools I think students should bookmark in 2026.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. GPA Calculator
&lt;/h2&gt;

&lt;p&gt;🔗 &lt;a href="https://freetoolforge.org/tools/calculators/gpa-calculator" rel="noopener noreferrer"&gt;https://freetoolforge.org/tools/calculators/gpa-calculator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Probably one of the most useful tools for university students.&lt;/p&gt;

&lt;p&gt;Instead of calculating GPA manually, this tool helps students quickly estimate semester performance.&lt;/p&gt;

&lt;p&gt;Useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Semester GPA calculation&lt;/li&gt;
&lt;li&gt;Credit hour tracking&lt;/li&gt;
&lt;li&gt;Academic planning&lt;/li&gt;
&lt;li&gt;Scholarship eligibility&lt;/li&gt;
&lt;li&gt;Performance analysis&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple tool, but students use it constantly throughout university life.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. CGPA to Percentage Calculator
&lt;/h2&gt;

&lt;p&gt;🔗 &lt;a href="https://freetoolforge.org/tools/calculators/cgpa-to-percentage" rel="noopener noreferrer"&gt;https://freetoolforge.org/tools/calculators/cgpa-to-percentage&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is especially valuable for Pakistani students.&lt;/p&gt;

&lt;p&gt;Many universities, internships, and companies still require percentage-based grading systems.&lt;/p&gt;

&lt;p&gt;The tool instantly converts CGPA into percentage format for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Job applications&lt;/li&gt;
&lt;li&gt;University admissions&lt;/li&gt;
&lt;li&gt;Scholarship forms&lt;/li&gt;
&lt;li&gt;Internship applications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Very practical and saves time.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. PDF Compressor
&lt;/h2&gt;

&lt;p&gt;🔗 &lt;a href="https://freetoolforge.org/tools/document-tools/pdf-compressor" rel="noopener noreferrer"&gt;https://freetoolforge.org/tools/document-tools/pdf-compressor&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every student eventually faces the same problem:&lt;/p&gt;

&lt;p&gt;“File size exceeds upload limit.”&lt;/p&gt;

&lt;p&gt;This tool helps compress PDF files quickly without needing complicated desktop software.&lt;/p&gt;

&lt;p&gt;Useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Assignment submissions&lt;/li&gt;
&lt;li&gt;Research papers&lt;/li&gt;
&lt;li&gt;Thesis uploads&lt;/li&gt;
&lt;li&gt;Resume uploads&lt;/li&gt;
&lt;li&gt;Government applications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A genuine lifesaver during deadlines.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. AI Grammar Checker
&lt;/h2&gt;

&lt;p&gt;🔗 &lt;a href="https://freetoolforge.org/tools/text-tools/ai-grammar-checker" rel="noopener noreferrer"&gt;https://freetoolforge.org/tools/text-tools/ai-grammar-checker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Good communication matters more than most students realize.&lt;/p&gt;

&lt;p&gt;Whether you're writing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Assignments&lt;/li&gt;
&lt;li&gt;Emails&lt;/li&gt;
&lt;li&gt;LinkedIn posts&lt;/li&gt;
&lt;li&gt;CVs&lt;/li&gt;
&lt;li&gt;Reports&lt;/li&gt;
&lt;li&gt;Freelance proposals&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Grammar quality affects professionalism.&lt;/p&gt;

&lt;p&gt;This AI Grammar Checker helps improve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sentence clarity&lt;/li&gt;
&lt;li&gt;Grammar mistakes&lt;/li&gt;
&lt;li&gt;Writing flow&lt;/li&gt;
&lt;li&gt;Readability&lt;/li&gt;
&lt;li&gt;Professional tone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Extremely useful for students preparing for internships and remote work opportunities.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Word Counter
&lt;/h2&gt;

&lt;p&gt;🔗 &lt;a href="https://freetoolforge.org/tools/text-tools/word-counter" rel="noopener noreferrer"&gt;https://freetoolforge.org/tools/text-tools/word-counter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A surprisingly important productivity tool.&lt;/p&gt;

&lt;p&gt;Most universities have strict word count requirements for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Essays&lt;/li&gt;
&lt;li&gt;Reports&lt;/li&gt;
&lt;li&gt;Research papers&lt;/li&gt;
&lt;li&gt;Summaries&lt;/li&gt;
&lt;li&gt;Assignments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This tool helps track:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Word count&lt;/li&gt;
&lt;li&gt;Character count&lt;/li&gt;
&lt;li&gt;Reading time&lt;/li&gt;
&lt;li&gt;Writing statistics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simple but essential.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Image Compressor
&lt;/h2&gt;

&lt;p&gt;🔗 &lt;a href="https://freetoolforge.org/tools/image-tools/image-compressor" rel="noopener noreferrer"&gt;https://freetoolforge.org/tools/image-tools/image-compressor&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Students constantly upload images for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Portfolios&lt;/li&gt;
&lt;li&gt;Assignments&lt;/li&gt;
&lt;li&gt;CVs&lt;/li&gt;
&lt;li&gt;Project documentation&lt;/li&gt;
&lt;li&gt;Freelance profiles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Compressed images help:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduce upload size&lt;/li&gt;
&lt;li&gt;Improve website speed&lt;/li&gt;
&lt;li&gt;Save storage&lt;/li&gt;
&lt;li&gt;Improve SEO for portfolios&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Especially useful for students learning web development and e-commerce.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. QR Code Generator
&lt;/h2&gt;

&lt;p&gt;🔗 &lt;a href="https://freetoolforge.org/tools/developer-tools/qr-code-generator" rel="noopener noreferrer"&gt;https://freetoolforge.org/tools/developer-tools/qr-code-generator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A very underrated student tool.&lt;/p&gt;

&lt;p&gt;Useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Portfolio sharing&lt;/li&gt;
&lt;li&gt;Event registration&lt;/li&gt;
&lt;li&gt;Resume links&lt;/li&gt;
&lt;li&gt;GitHub projects&lt;/li&gt;
&lt;li&gt;Google Drive sharing&lt;/li&gt;
&lt;li&gt;Presentation links&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Students can instantly create QR codes for digital projects and presentations.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Password Generator
&lt;/h2&gt;

&lt;p&gt;🔗 &lt;a href="https://freetoolforge.org/tools/developer-tools/password-generator" rel="noopener noreferrer"&gt;https://freetoolforge.org/tools/developer-tools/password-generator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Students manage multiple accounts daily:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;University portals&lt;/li&gt;
&lt;li&gt;LMS dashboards&lt;/li&gt;
&lt;li&gt;Freelance platforms&lt;/li&gt;
&lt;li&gt;GitHub&lt;/li&gt;
&lt;li&gt;Email accounts&lt;/li&gt;
&lt;li&gt;Banking apps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using weak passwords is risky.&lt;/p&gt;

&lt;p&gt;This tool helps generate secure passwords quickly and safely.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Online Compiler
&lt;/h2&gt;

&lt;p&gt;🔗 &lt;a href="https://freetoolforge.org/tools/developer-tools/online-compiler" rel="noopener noreferrer"&gt;https://freetoolforge.org/tools/developer-tools/online-compiler&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A great tool for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CS students&lt;/li&gt;
&lt;li&gt;Programming beginners&lt;/li&gt;
&lt;li&gt;Quick debugging&lt;/li&gt;
&lt;li&gt;Coding practice&lt;/li&gt;
&lt;li&gt;Interview preparation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of setting up heavy local environments, students can quickly test snippets directly in the browser.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Browser-Based Productivity Tools Are Growing Fast
&lt;/h2&gt;

&lt;p&gt;Students today value speed and convenience.&lt;/p&gt;

&lt;p&gt;The biggest advantages are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No installation required&lt;/li&gt;
&lt;li&gt;Mobile friendly&lt;/li&gt;
&lt;li&gt;Free access&lt;/li&gt;
&lt;li&gt;Fast workflow&lt;/li&gt;
&lt;li&gt;Easy to use&lt;/li&gt;
&lt;li&gt;Works anywhere&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of bookmarking 20 different websites, platforms like ToolForge combine everything into one clean productivity ecosystem.&lt;/p&gt;

&lt;p&gt;For students balancing academics, internships, freelancing, and online learning, these small tools genuinely save time every week.&lt;/p&gt;

&lt;p&gt;Main website:&lt;br&gt;
🔗 &lt;a href="https://freetoolforge.org" rel="noopener noreferrer"&gt;https://freetoolforge.org&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What productivity tools do you use most frequently as a student?&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
