<?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: Shakeel Skl</title>
    <description>The latest articles on DEV Community by Shakeel Skl (@shakeel_skl_019683a0a1836).</description>
    <link>https://dev.to/shakeel_skl_019683a0a1836</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3839873%2Fe7a5c4c8-1481-4c37-872b-143b2ddcf063.png</url>
      <title>DEV Community: Shakeel Skl</title>
      <link>https://dev.to/shakeel_skl_019683a0a1836</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shakeel_skl_019683a0a1836"/>
    <language>en</language>
    <item>
      <title>QR Codes in 2026: Skip the Spam, Keep Your Privacy, and Generate for Free</title>
      <dc:creator>Shakeel Skl</dc:creator>
      <pubDate>Fri, 15 May 2026 21:24:42 +0000</pubDate>
      <link>https://dev.to/shakeel_skl_019683a0a1836/qr-codes-in-2026-skip-the-spam-keep-your-privacy-and-generate-for-free-5fja</link>
      <guid>https://dev.to/shakeel_skl_019683a0a1836/qr-codes-in-2026-skip-the-spam-keep-your-privacy-and-generate-for-free-5fja</guid>
      <description>&lt;p&gt;Walk into any coffee shop, glance at a concert poster, scan a restaurant table tent, or check the back of a business card — chances are, you'll spot a QR code staring back at you. In 2026, QR codes are no longer a quirky tech trend. They are a mainstream, everyday tool that billions of people use without a second thought. From contactless payments to instant website access, QR codes have become the invisible bridge between the physical world and the digital one.&lt;/p&gt;

&lt;p&gt;Despite how common they are, most people still don't know how easy it is to create their own. Whether you want to generate QR codes for marketing materials or quickly create a QR code online for personal use, a reliable QR code maker can save you time, money, and hassle — and you don't need to pay a dime.&lt;/p&gt;

&lt;p&gt;In this guide, we'll break down everything you need to know about QR codes, explore the most creative ways businesses are using them in 2026, and show you why SolveBar's free QR code generator is the simplest, completely private, and most powerful tool available online today.&lt;/p&gt;

&lt;p&gt;What Are QR Codes?&lt;br&gt;
QR codes (short for Quick Response codes) are two-dimensional barcodes that store a wide range of data — from plain text and website URLs to contact information and Wi-Fi network credentials. Originally invented in 1994 by Denso Wave for tracking automotive parts, they were designed to be scanned at high speed.&lt;/p&gt;

&lt;p&gt;Unlike traditional barcodes that hold only a small amount of data in a single line, QR codes use a grid of squares arranged both horizontally and vertically, holding up to several thousand characters.&lt;/p&gt;

&lt;p&gt;When someone scans a QR code with their smartphone camera (built-in on virtually every modern device — no app required), the encoded data is instantly decoded: opening a web page, saving a contact, joining a Wi-Fi network, or initiating a payment.&lt;/p&gt;

&lt;p&gt;Three key features make QR codes especially powerful:&lt;/p&gt;

&lt;p&gt;Speed — They scan in a fraction of a second.&lt;br&gt;
Capacity — They encode far more data than traditional barcodes.&lt;br&gt;
Error Correction — Even if part of the code is damaged or obscured (up to ~30%), it still reads successfully. This is what makes it possible to add logos and custom designs without breaking them.&lt;br&gt;
Creative Use Cases for QR Codes in 2026&lt;br&gt;
Business Cards That Actually Get Used&lt;br&gt;
Paper business cards are easy to lose and hard to update. A vCard QR code on your card lets anyone scan and instantly save your full contact details — name, phone, email, company, website — directly to their phone's address book. No typing, no errors, no "I'll add you later" that never happens.&lt;/p&gt;

&lt;p&gt;Restaurant Menus That Never Go Out of Date&lt;br&gt;
Restaurants place QR codes on tables, signage, and takeout bags linking directly to their online menu. When prices change or seasonal items rotate, the menu updates instantly — no costly reprints.&lt;/p&gt;

&lt;p&gt;Wi-Fi Sharing Without the Friction&lt;br&gt;
How many times have you spelled out your Wi-Fi password while a guest taps it in wrong three times? A Wi-Fi QR code eliminates all of that. Guests point their camera at the code and they're connected instantly.&lt;/p&gt;

&lt;p&gt;Event Tickets, Flyers, and Conference Badges&lt;br&gt;
Event organizers love QR codes for marketing and logistics. A flyer code links to registration, a ticket code enables instant check-in, and conference badges let attendees exchange contact info with a single scan.&lt;/p&gt;

&lt;p&gt;Contactless Payments and Tipping&lt;br&gt;
From farmers' market stalls to street musicians, QR codes have democratized digital payments. Vendors generate a QR code linking to their payment page, and customers scan to pay. Restaurants also place QR codes on receipts to make tipping effortless.&lt;/p&gt;

&lt;p&gt;Product Packaging and Customer Support&lt;br&gt;
Brands place QR codes on packaging that link to user manuals, tutorial videos, warranty registration, or support chats — improving the customer experience while reducing printing costs.&lt;/p&gt;

&lt;p&gt;Why Your Business Needs a QR Code Generator Right Now&lt;br&gt;
If you're running a business in 2026, QR codes are one of the lowest-cost, highest-impact tools in your marketing toolkit — and a solid QR code generator tool makes creating them effortless.&lt;/p&gt;

&lt;p&gt;First, they bridge offline and online. A QR code on a flyer or poster gives customers an instant, frictionless path to your website. No one types a long URL on their phone, but everyone will scan a QR code.&lt;/p&gt;

&lt;p&gt;Second, they look professional. A custom QR code signals that your brand is modern and tech-savvy.&lt;/p&gt;

&lt;p&gt;Third, they're cost-effective. Generate one for free, print it on existing materials, and it works indefinitely.&lt;/p&gt;

&lt;p&gt;Finally, they're universally compatible. Every smartphone from the last several years has a built-in QR scanner.&lt;/p&gt;

&lt;p&gt;SolveBar's Free QR Code Generator: Everything You Need, Nothing You Don't&lt;br&gt;
There's no shortage of QR code generators online, but finding a genuinely free online QR code generator with no strings attached is surprisingly difficult. Most come with frustrating limitations — watermarks, resolution caps unless you pay, mandatory account creation, or hidden tracking. Many people searching for a QR code generator no watermark or the best free QR code generator end up disappointed. SolveBar's QR code creator takes a completely different approach.&lt;/p&gt;

&lt;p&gt;Multiple QR Code Types&lt;br&gt;
SolveBar supports all the most popular formats: URL QR codes (perfect for anyone looking to convert a URL to QR code), plain text QR codes, Wi-Fi QR codes (encode network name, password, and encryption type for instant connections), and vCard QR codes (embed full contact information for easy sharing).&lt;/p&gt;

&lt;p&gt;Full Customization&lt;br&gt;
SolveBar lets you customize foreground and background colors so your QR code blends seamlessly with your branding. You can also adjust the error correction level for resilience.&lt;/p&gt;

&lt;p&gt;High-Quality Downloads in PNG and SVG&lt;br&gt;
Download as PNG for crisp digital and small-print use, or grab the SVG for scalable large-format printing. Both are high-resolution and ready to go.&lt;/p&gt;

&lt;p&gt;No Watermarks, No Hidden Costs&lt;br&gt;
Every QR code generated on SolveBar is 100% free and completely watermark-free. No premium tiers, no paywalls, no "upgrade to remove watermark" tricks. What you see is what you get — a clean, professional QR code you own and can use anywhere.&lt;/p&gt;

&lt;p&gt;No Account Required&lt;br&gt;
No sign-up, no login, no email address required. Visit the tool, enter your data, customize your design, and download. It takes less than thirty seconds from start to finish.&lt;/p&gt;

&lt;p&gt;Bulk/Batch QR Code Generation (ZIP Download)&lt;br&gt;
Need to generate codes for an entire product catalog or a list of event attendees? SolveBar offers a powerful batch generation feature. Simply upload your data, and SolveBar will generate all your custom QR codes at once, neatly packaged in a downloadable ZIP file. It’s a premium feature offered completely for free.&lt;/p&gt;

&lt;p&gt;Privacy-First: Your Data Stays Yours&lt;br&gt;
In an era where every online tool wants your email and personal data, SolveBar takes a refreshingly simple approach. When you use the QR code generator, nothing is tracked. No analytics, no user accounts, no server-side logs, no data retention.&lt;/p&gt;

&lt;p&gt;Completely Browser-Based Processing&lt;br&gt;
Your QR code data is processed entirely in your browser. The URL, text, Wi-Fi credentials, or contact information you encode never touches SolveBar's servers — it's generated locally on your device and downloaded directly to your hard drive. This makes SolveBar ideal for sensitive information like internal company links, private Wi-Fi passwords, or personal contact details.&lt;/p&gt;

&lt;p&gt;100% Ad-Free Experience&lt;br&gt;
Tired of generators that bombard you with pop-ups and spammy ads? SolveBar is completely ad-free. We don't monetize your attention.&lt;/p&gt;

&lt;p&gt;No tracking. No ads. No data harvesting. Just a fast, clean tool that does exactly what it promises.&lt;/p&gt;

&lt;p&gt;Frequently Asked Questions&lt;br&gt;
Q: Is SolveBar's QR code generator really free?&lt;br&gt;
A: Yes, completely free. There are no hidden fees, no premium plans, and no watermarks on any QR code you generate. Every feature—including batch generation—is available at no cost.&lt;/p&gt;

&lt;p&gt;Q: How does batch generation work?&lt;br&gt;
A: You can input multiple URLs or data points at once, and SolveBar will instantly generate all the corresponding QR codes, allowing you to download them all together in a single, organized ZIP file.&lt;/p&gt;

&lt;p&gt;Q: Do QR codes expire?&lt;br&gt;
A: QR codes generated with SolveBar are static — the data is encoded directly into the image itself. They never expire and will work forever as long as the linked content (like a website URL) remains active.&lt;/p&gt;

&lt;p&gt;Q: Can I customize the colors of my QR code?&lt;br&gt;
A: Absolutely. SolveBar lets you choose custom foreground and background colors so your QR code matches your branding. Just ensure sufficient contrast between the two colors for reliable scanning.&lt;/p&gt;

&lt;p&gt;Q: What's the difference between PNG and SVG formats?&lt;br&gt;
A: PNG is a raster format great for digital use and small-to-medium print sizes. SVG is a vector format that scales to any size without losing quality, ideal for banners, posters, and signage. SolveBar provides both.&lt;/p&gt;

&lt;p&gt;Q: Is my data safe?&lt;br&gt;
A: Extremely safe. Because SolveBar is entirely browser-based, your data never leaves your computer. There is zero server-side data retention.&lt;/p&gt;

&lt;p&gt;Q: Can I add a logo to the center of my QR code?&lt;br&gt;
A: SolveBar focuses on producing clean, highly scannable codes. If you need an embedded logo, download the SVG file and overlay your logo using any design tool — just keep the logo small enough that the code remains fully functional.&lt;/p&gt;

&lt;p&gt;Start Creating Your QR Codes Today&lt;br&gt;
Whether you're streamlining a restaurant menu, making networking easier with vCard codes, sharing Wi-Fi with guests, or driving traffic to a landing page, QR codes are the simplest tool with the biggest impact. With SolveBar's free QR code generator, you can create professional, custom, watermark-free QR codes in seconds — no account, no cost, no ads, and absolute privacy.&lt;/p&gt;

&lt;p&gt;Visit solvebar tools qr-code-generator to start generating your first QR code right now.&lt;/p&gt;

</description>
      <category>qrcode</category>
      <category>nologin</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Wallet Age: Your Invisible Shield Against Crypto Scams</title>
      <dc:creator>Shakeel Skl</dc:creator>
      <pubDate>Sun, 10 May 2026 17:56:00 +0000</pubDate>
      <link>https://dev.to/shakeel_skl_019683a0a1836/wallet-age-your-invisible-shield-against-crypto-scams-2ojc</link>
      <guid>https://dev.to/shakeel_skl_019683a0a1836/wallet-age-your-invisible-shield-against-crypto-scams-2ojc</guid>
      <description>&lt;p&gt;Wallet Age Checker: How to Verify Any Crypto Wallet Before You Send Funds&lt;/p&gt;

&lt;p&gt;In cryptocurrency, trust is everything — and it is also the thing in shortest supply. There is no bank to call, no fraud department to escalate to, and no chargeback button when something goes wrong. When you send tokens to an address, that transaction is final. That is why seasoned crypto users check how old a wallet is before engaging with it.&lt;/p&gt;

&lt;p&gt;A wallet created last week holding millions of dollars tells a very different story from one active since 2017. Scammers, bot networks, airdrop farmers, and Sybil attackers all share one trait — their wallets tend to be brand new. A reliable &lt;strong&gt;free wallet age checker&lt;/strong&gt; is the fastest way to expose that discrepancy.&lt;/p&gt;

&lt;p&gt;Why Should You Check Wallet Age?&lt;/p&gt;

&lt;p&gt;Every Ethereum address is publicly visible on the blockchain. Anyone can see its balance, transaction history, and — critically — when it was first created. That creation date is one of the simplest yet most revealing signals available. Here are the primary reasons to check it:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Detect Scams and Fraudulent Addresses&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Scammers create fresh wallets for each campaign. Whether running a phishing site, promoting a fake token presale, or impersonating a project member, the wallet they share will almost always be days or weeks old. If someone claims to be a long-standing community member but their wallet was created yesterday, that is a red flag.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Verify Trust and Credibility&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On the flip side, a wallet active for years with consistent, organic transaction patterns signals legitimacy. Developers who have been building since 2018 and early adopters who held through multiple market cycles leave a visible trail. A crypto wallet age lookup lets you confirm that trail exists — which is exactly what a &lt;strong&gt;crypto wallet checker&lt;/strong&gt; helps you do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Identify Bot and Sybil Activity&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Many DeFi protocols distribute rewards based on wallet activity. Bad actors create hundreds or thousands of new wallets to game these systems — a technique known as a Sybil attack. When you see a cluster of wallets all created on the same day with identical patterns, that is a strong indicator of coordinated manipulation. Being able to check wallet creation date at scale helps researchers identify these networks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Uncover Airdrop Farming&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Airdrop farmers mint wallets in bulk, perform minimal interactions to qualify for token distributions, then dump immediately. These farming wallets are typically young with no meaningful history beyond the target protocol. Traders who check wallet age before buying a newly airdropped token can spot when supply is dominated by farmers rather than genuine users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Personal Due Diligence&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Whether evaluating a counterparty in an OTC deal or responding to a stranger who slid into your DMs with a "great opportunity," checking their wallet age is a basic safety step that takes seconds and can save you thousands.&lt;/p&gt;

&lt;p&gt;How Wallet Age Helps You Make Better Decisions&lt;/p&gt;

&lt;p&gt;Understanding wallet age is about adding context. Here is how to interpret the data:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Older wallets (2+ years)&lt;/strong&gt; are generally more trustworthy. Consistent, varied transaction history suggests a real user genuinely participating in the ecosystem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New wallets with large balances&lt;/strong&gt; are one of the most suspicious signals in crypto. If a wallet was created last month and is suddenly moving hundreds of thousands of dollars, ask yourself why. It could be a legitimate new user — or a scammer consolidating funds before disappearing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clusters of same-day wallets&lt;/strong&gt; almost always indicate bot activity or coordinated manipulation. If 40% of a token's holders created wallets within a three-day window, proceed with extreme caution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wallets with no history before a specific event&lt;/strong&gt; — like a token launch or airdrop snapshot — are likely farming wallets.&lt;/p&gt;

&lt;p&gt;Wallet age is a &lt;strong&gt;leading indicator&lt;/strong&gt; — it gives you information before a transaction happens, not after. Using a &lt;strong&gt;blockchain wallet age&lt;/strong&gt; tool as part of your workflow turns a simple data point into actionable intelligence.&lt;/p&gt;

&lt;p&gt;Who Benefits from a Wallet Age Checker?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Crypto Traders&lt;/strong&gt; can evaluate counterparty risk and avoid tokens where farming dominance skews the supply. A quick ethereum wallet age check before entering a trade adds protection most retail traders overlook. If you want to &lt;strong&gt;check eth wallet age&lt;/strong&gt; quickly, a dedicated lookup tool saves time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NFT Collectors&lt;/strong&gt; face wash trading, fake minting, and bot-driven floor price manipulation. Checking the age of wallets you buy from or sell to helps you avoid artificial market activity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DeFi Users&lt;/strong&gt; interact with smart contracts daily and need to verify that the addresses they engage with have a legitimate history. A wallet creation date lookup reveals whether a protocol's deployer wallet has earned community trust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security Researchers&lt;/strong&gt; use wallet age as one signal among many to map attack patterns, trace stolen funds, and identify coordinated fraud networks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project Founders and DAO Members&lt;/strong&gt; use wallet age data to qualify participants in governance votes and token distributions, reducing Sybil attacks and farming.&lt;/p&gt;

&lt;p&gt;SolveBar Wallet Age Checker: Instant Answers, Zero Hassle&lt;/p&gt;

&lt;p&gt;Most blockchain explorers are cluttered with ads, require account creation, or bury the data you need. SolveBar takes a different approach.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;SolveBar Wallet Age Checker&lt;/strong&gt; (solvebar.com/tools/wallet-age-checker) is a completely free, web-based tool that gives you the wallet creation data you need in seconds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Enter Any Address&lt;/strong&gt;: Paste an Ethereum wallet address into the search field. That is it. No formatting requirements, no special steps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instant Creation Date&lt;/strong&gt;: See exactly when the wallet was first created, displayed in a clear, human-readable format.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First Transaction Timestamp&lt;/strong&gt;: Get the date and time of the wallet's very first on-chain activity — the &lt;strong&gt;wallet first transaction&lt;/strong&gt; — so you can verify activity age independently of account creation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Chain Support&lt;/strong&gt;: Works with Ethereum and other major EVM-compatible networks, giving you flexibility across the ecosystem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean, Ad-Free Interface&lt;/strong&gt;: No pop-ups, no sponsored content, no distractions. Just the data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whether you are a seasoned analyst or a first-time user checking if someone is legitimate, SolveBar delivers accurate results with zero friction — making it the &lt;strong&gt;best wallet age checker&lt;/strong&gt; available.&lt;/p&gt;

&lt;p&gt;Your Privacy Is Non-Negotiable&lt;/p&gt;

&lt;p&gt;In crypto, privacy and security are inseparable. Many blockchain tools require wallet connections, accounts, or tracking cookies to show you basic public data. SolveBar rejects that model entirely.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No login or account required&lt;/strong&gt; — visit the page, enter an address, get your result. That is the entire workflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No queries are stored or tracked&lt;/strong&gt; — SolveBar does not log the wallet addresses you search, your IP address, or any personal information. Your research remains your business.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No wallet connection needed&lt;/strong&gt; — SolveBar never asks for your private keys, seed phrase, or any connection to your crypto accounts. It is a read-only lookup tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No ads&lt;/strong&gt; — the experience is clean, fast, and focused entirely on delivering the information you asked for.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you use a wallet age tool, you are already doing the responsible thing. The tool should respect that — not exploit it.&lt;/p&gt;

&lt;p&gt;Frequently Asked Questions&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Is the SolveBar Wallet Age Checker really free?&lt;/strong&gt;&lt;br&gt;
A: Yes, 100% free. There are no premium tiers, hidden fees, or paywalls. Every feature is available to every visitor without creating an account or providing any personal information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: How does a wallet age checker work?&lt;/strong&gt;&lt;br&gt;
A: The tool queries public blockchain data to find the first block in which a given wallet address appeared. That block's timestamp reveals the wallet's creation date and first transaction time. Since all Ethereum transactions are publicly recorded, this data is available for any address — and SolveBar retrieves it instantly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Can I check wallet age for networks other than Ethereum?&lt;/strong&gt;&lt;br&gt;
A: Yes. SolveBar supports Ethereum and several major EVM-compatible chains, with more added regularly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Does SolveBar store or track the addresses I search?&lt;/strong&gt;&lt;br&gt;
A: No. SolveBar does not log searched addresses, store query history, or track your IP. Your lookups are completely private.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Can a scammer fake their wallet age?&lt;/strong&gt;&lt;br&gt;
A: A wallet's creation date is recorded immutably on the blockchain and cannot be faked or backdated. However, scammers sometimes purchase older wallets to appear credible. This is why wallet age should be one of several signals you consider — not the only one.&lt;/p&gt;

&lt;p&gt;Check Before You Trust&lt;/p&gt;

&lt;p&gt;In a market where billions are lost to scams every year, the simplest tools are often the most powerful. A thorough &lt;strong&gt;crypto wallet lookup&lt;/strong&gt; that includes wallet age is one of the fastest ways to separate legitimate participants from bad actors. Checking a wallet's age takes seconds and can reveal red flags that save you from costly mistakes.&lt;/p&gt;

&lt;p&gt;Try the &lt;strong&gt;SolveBar Wallet Age Checker&lt;/strong&gt; at solvebar.com/tools/wallet-age-checker — completely free, no login, no tracking, no ads. Just paste an address and get the answers you need.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>cryptocurrency</category>
      <category>web3</category>
      <category>security</category>
    </item>
    <item>
      <title>Password Generator: Create Unbreakable Passwords in Seconds</title>
      <dc:creator>Shakeel Skl</dc:creator>
      <pubDate>Fri, 08 May 2026 09:52:16 +0000</pubDate>
      <link>https://dev.to/shakeel_skl_019683a0a1836/password-generator-create-unbreakable-passwords-in-seconds-1pf2</link>
      <guid>https://dev.to/shakeel_skl_019683a0a1836/password-generator-create-unbreakable-passwords-in-seconds-1pf2</guid>
      <description>&lt;p&gt;Your passwords are the front door to your digital life. Every account you own — from online banking and email to social media and cloud storage — sits behind a string of characters that, in many cases, is far weaker than it should be. The uncomfortable truth is that most people are still using passwords that can be cracked in minutes.&lt;/p&gt;

&lt;p&gt;Over 80% of data breaches involve stolen or weak credentials. The password "123456" still tops the list of most-used passwords worldwide, followed closely by "password" and "qwerty." If any of yours resemble these, your accounts are essentially sitting unlocked.&lt;/p&gt;

&lt;p&gt;A single compromised password can cascade into identity theft, financial loss, and a nightmare of recovering accounts across dozens of platforms. Protecting yourself doesn't require technical expertise — it starts with understanding what makes a password truly strong and using the right tools to create one. That's where a reliable password generator comes in, and SolveBar's password generator tool makes it effortless.&lt;/p&gt;

&lt;p&gt;What Makes a Strong Password?&lt;/p&gt;

&lt;p&gt;A strong password isn't just "hard to guess." It's built on four pillars that resist both human guessing and machine-powered cracking.&lt;/p&gt;

&lt;p&gt;Length matters more than complexity. A 16-character password of random lowercase letters is significantly harder to crack than an 8-character password packed with symbols. Every additional character exponentially increases the number of possible combinations.&lt;/p&gt;

&lt;p&gt;Complexity adds another layer. Mixing uppercase and lowercase letters, numbers, and special characters like !@#$% expands the character pool an attacker must search through — a cornerstone of password security.&lt;/p&gt;

&lt;p&gt;Uniqueness is critical. No two accounts should share the same password. If one service suffers a breach, your reused password becomes a master key for attackers. Credential stuffing attacks rely entirely on password reuse.&lt;/p&gt;

&lt;p&gt;Entropy measures true randomness. A password like "CorrectHorseBatteryStaple" might look long, but because the words come from a known list, its entropy is limited. A truly random string like "xK9#mP2$vL7nQ4wR" has far higher entropy and is exponentially more resistant to cracking. Generating that kind of randomness by hand is practically impossible — which is exactly why a secure password generator is essential.&lt;/p&gt;

&lt;p&gt;Common Password Mistakes You're Probably Making&lt;/p&gt;

&lt;p&gt;Even security-conscious people often fall into traps that undermine their password strength.&lt;/p&gt;

&lt;p&gt;The biggest offender is password reuse. Creating one "good" password and using it everywhere is tempting, but reuse is a ticking time bomb. When a service gets breached, your reused password becomes a master key for attackers. The LinkedIn breach of 2012 exposed 117 million passwords still in use on other sites years later.&lt;/p&gt;

&lt;p&gt;Another common mistake is relying on predictable patterns. Substituting "3" for "E" and "0" for "O" in a word like "P@ssw0rd" might feel clever, but cracking tools have accounted for these substitutions for decades. Keyboard walks like "qwerty123" are cracked almost instantly.&lt;/p&gt;

&lt;p&gt;Using personal information is equally dangerous. Birthdates, pet names, and hometowns are easily discoverable through social media. Attackers routinely mine social profiles to build targeted dictionaries.&lt;/p&gt;

&lt;p&gt;Many people also change passwords by incrementing a number at the end — "Password1," "Password2," "Password3." This practice is now recognized as counterproductive because it produces passwords only marginally different from their predecessors.&lt;/p&gt;

&lt;p&gt;Why You Need a Password Generator&lt;/p&gt;

&lt;p&gt;Humans are terrible at creating random passwords. Our brains are wired to find patterns, remember associations, and create meaning — wonderful traits for storytelling, but exactly what makes us bad at generating secure passwords.&lt;/p&gt;

&lt;p&gt;When you create a password yourself, you unconsciously lean toward words you know and patterns you recognize. A truly strong password should look like gibberish, because it is — a random string with no underlying logic and no emotional connection to help you remember it.&lt;/p&gt;

&lt;p&gt;A password generator eliminates this human bias entirely. It uses cryptographic algorithms to produce genuinely random sequences with maximum entropy, meaning no pattern for an attacker to exploit.&lt;/p&gt;

&lt;p&gt;A good password maker puts you in control. SolveBar's random password generator lets you decide the length, choose which character types to include, and generate as many passwords as you need — all in seconds. No software to install, no account to create, and no cost involved. It's the best password generator for anyone who needs secure passwords that meet the highest security standards.&lt;/p&gt;

&lt;p&gt;How to Use SolveBar's Password Generator&lt;/p&gt;

&lt;p&gt;Looking to create password online quickly and safely? Using SolveBar's free password generator is straightforward, even if you've never used one before. Visit solvebar.com/password-generator and you'll find a clean, distraction-free interface designed to do one thing exceptionally well: generate secure passwords.&lt;/p&gt;

&lt;p&gt;Start by selecting your desired password length using the slider. For most accounts, 16 characters is a solid default. For high-security accounts like banking or primary email, consider pushing it to 20 or more. There's no upper limit that becomes unwieldy — longer is always stronger.&lt;/p&gt;

&lt;p&gt;Next, choose which character types to include. You can enable or disable uppercase letters, lowercase letters, numbers, and special characters independently. Most security experts recommend using all four categories for maximum strength. The tool shows you a real-time preview of your password as you adjust these settings.&lt;/p&gt;

&lt;p&gt;Click the generate button, and your password appears instantly. You can copy it to your clipboard with a single click. Need another option? Generate again. Want several at once? The tool makes it easy to create multiple passwords in rapid succession.&lt;/p&gt;

&lt;p&gt;What you won't find are confusing options, unnecessary features, or a cluttered interface. SolveBar keeps things focused so you can generate a password and get on with your day. It's free online password generation at its simplest and most effective — a truly free online password generator you can trust.&lt;/p&gt;

&lt;p&gt;The Privacy Advantage: Your Data Never Leaves Your Device&lt;/p&gt;

&lt;p&gt;Most online tools come with a hidden cost: your data. SolveBar's password generator takes a fundamentally different approach.&lt;/p&gt;

&lt;p&gt;The entire tool runs locally in your browser. Computation happens on your device — not on a remote server. No data is sent anywhere. There's no login required, no tracking scripts, zero ads, and zero third-party analytics. The password you generate exists only on your screen until you copy or save it yourself.&lt;/p&gt;

&lt;p&gt;Your generated passwords are never transmitted, stored, logged, or accessible to anyone — including SolveBar. In an era where data harvesting is the default business model, this transparency is rare. SolveBar is 100% free, no login, no data sent to servers, no ads, fully private — and that's not just a marketing tagline. It's the technical architecture of the tool.&lt;/p&gt;

&lt;p&gt;Frequently Asked Questions&lt;/p&gt;

&lt;p&gt;Is it safe to use an online password generator?&lt;/p&gt;

&lt;p&gt;It depends on the tool. SolveBar's password generator runs entirely in your browser using client-side JavaScript. No passwords are transmitted to any server. You can verify this by using browser developer tools and checking network activity while generating a password. Because the tool requires no login and sends no data externally, it's as safe as a locally installed application — without the installation.&lt;/p&gt;

&lt;p&gt;How long should my password be?&lt;/p&gt;

&lt;p&gt;For most accounts, 12 to 16 characters is the current security recommendation. For sensitive accounts such as banking, email, and password managers, 20 characters or more is ideal. The longer the password, the more resistant it is to brute-force attacks. SolveBar's tool allows you to set any length, so you can easily scale up for high-value accounts.&lt;/p&gt;

&lt;p&gt;Can I customize which characters are included?&lt;/p&gt;

&lt;p&gt;Yes. SolveBar lets you toggle uppercase letters, lowercase letters, numbers, and special characters on or off independently. You can mix and match based on the requirements of the service you're signing up for — some websites restrict which special characters are allowed, and the tool gives you the flexibility to adapt.&lt;/p&gt;

&lt;p&gt;Where should I store my generated passwords?&lt;/p&gt;

&lt;p&gt;The most secure option is a dedicated password manager such as Bitwarden, 1Password, or KeePass. These tools encrypt your password vault and let you access your credentials with a single master password. Avoid storing passwords in plain text files, spreadsheets, browser autofill, or sticky notes. A password manager combined with a password generator is the gold standard for personal security.&lt;/p&gt;

&lt;p&gt;Is there a limit to how many passwords I can generate?&lt;/p&gt;

&lt;p&gt;No. SolveBar's free password generator has no usage limits, no quotas, and no paywalls. You can generate as many passwords as you need, as often as you need them, without ever being asked to create an account or provide payment information.&lt;/p&gt;

&lt;p&gt;Start Building Stronger Passwords Today&lt;/p&gt;

&lt;p&gt;Weak passwords are the weakest link in your digital security. The tools to fix this problem are free, instant, and require zero technical skill. SolveBar's strong password generator gives you maximum-strength, cryptographically random passwords in seconds — with no login, no data sent to servers, no ads, and no compromise on privacy.&lt;/p&gt;

&lt;p&gt;Visit solvebar password generator and create your first truly secure password. Your future self will thank you.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>privacy</category>
      <category>security</category>
      <category>programming</category>
    </item>
    <item>
      <title>Stop pasting API keys into online formatters and croppers</title>
      <dc:creator>Shakeel Skl</dc:creator>
      <pubDate>Tue, 05 May 2026 19:41:27 +0000</pubDate>
      <link>https://dev.to/shakeel_skl_019683a0a1836/stop-pasting-api-keys-into-online-formatters-and-croppers-3n9b</link>
      <guid>https://dev.to/shakeel_skl_019683a0a1836/stop-pasting-api-keys-into-online-formatters-and-croppers-3n9b</guid>
      <description>&lt;p&gt;Every day, millions of developers copy-paste sensitive data into random online tools.&lt;/p&gt;

&lt;p&gt;A JSON response from a production server into an online formatter. A snippet containing a database connection string into a snippet manager. A UI design file from Figma into an online image cropper.&lt;/p&gt;

&lt;p&gt;We do it because it’s fast. But we rarely stop to ask: &lt;em&gt;where is this data actually going?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you paste a JSON payload with a live Bearer token into a standard online formatter, that token travels across the internet to a third-party server. If you upload a design file containing proprietary UI to an online cropper, that intellectual property is now sitting on someone else's hard drive.&lt;/p&gt;

&lt;p&gt;For side projects, we might not care. But for client work or proprietary code, this is a massive security hole.&lt;/p&gt;

&lt;h2&gt;The "Local-Only" Alternative&lt;/h2&gt;

&lt;p&gt;Over the last few months, I've been building a suite of browser-native tools that process everything locally. Because they use native JavaScript APIs, the data never triggers a network request.&lt;/p&gt;

&lt;p&gt;If you open your browser's DevTools (F12 -&amp;gt; Network tab) and use these tools, you will see exactly zero outbound HTTP requests.&lt;/p&gt;

&lt;p&gt;Here are the three I use the most:&lt;/p&gt;

&lt;h3&gt;1. A Private JSON Formatter&lt;/h3&gt;

&lt;p&gt;When you need to beautify or validate a JSON response, you shouldn't have to send your API keys to a server. I use a &lt;a href="https://solvebar.com/tools/json-formatter" rel="noopener noreferrer"&gt;private JSON formatter&lt;/a&gt; that runs entirely in the browser tab. It handles deeply nested objects and catches trailing comma errors without uploading the payload.&lt;/p&gt;

&lt;h3&gt;2. A Code Snippet Manager&lt;/h3&gt;

&lt;p&gt;Storing code snippets in Notion or GitHub Gists is fine for public stuff, but what about private client logic? I use a &lt;a href="https://solvebar.com/tools/code-snippet-manager" rel="noopener noreferrer"&gt;browser-based snippet manager&lt;/a&gt; that saves everything to &lt;code&gt;localStorage&lt;/code&gt;. It supports syntax highlighting for 20+ languages, requires zero login, and keeps your proprietary logic completely off the cloud.&lt;/p&gt;

&lt;h3&gt;3. A "Fig Cropper" and Image Tool&lt;/h3&gt;

&lt;p&gt;Designers often export UI frames from Figma and hand them off to developers. If you need to crop or resize them, uploading them to a random server is a privacy risk. I use an &lt;a href="https://solvebar.com/tools/image-cropper" rel="noopener noreferrer"&gt;image cropper&lt;/a&gt; that works as a Fig cropper—handling high-res design files directly in the browser. It also includes a built-in &lt;a href="https://solvebar.com/tools/image-converter" rel="noopener noreferrer"&gt;image converter&lt;/a&gt; to switch between JPG, PNG, and WebP without a server upload.&lt;/p&gt;

&lt;h2&gt;Why this matters for client work&lt;/h2&gt;

&lt;p&gt;If you are handling files for a healthcare client, a fintech startup, or a legally binding contract, sending that data to a random SaaS tool might actually violate your NDA or compliance requirements (like HIPAA or GDPR).&lt;/p&gt;

&lt;p&gt;Using local-only tools isn't just a "nice-to-have" privacy flex. It's a professional requirement.&lt;/p&gt;

&lt;p&gt;Next time you need to format, crop, or store something sensitive, check your Network tab. If you see data leaving your browser, close the tab and find a local alternative.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>private</category>
    </item>
    <item>
      <title>browser-native crypto utilities every Web3 dev needs</title>
      <dc:creator>Shakeel Skl</dc:creator>
      <pubDate>Sat, 18 Apr 2026 14:47:30 +0000</pubDate>
      <link>https://dev.to/shakeel_skl_019683a0a1836/browser-native-crypto-utilities-every-web3-dev-needs-lk2</link>
      <guid>https://dev.to/shakeel_skl_019683a0a1836/browser-native-crypto-utilities-every-web3-dev-needs-lk2</guid>
      <description>&lt;p&gt;Here is the complete material for your second dev.to article, targeting the crypto programmatic tools we built. &lt;/p&gt;

&lt;p&gt;Again, this is pure Markdown with angle brackets &lt;code&gt;&amp;lt; &amp;gt;&lt;/code&gt; around the URLs so dev.to doesn't break the links. I also included the strict single-word tags at the bottom.&lt;/p&gt;

&lt;p&gt;As a web dev, I've lost count of how many times a crypto project I was building needed a quick unit conversion or a data fetch. &lt;/p&gt;

&lt;p&gt;When you're working with Web3.js, Ethers, or just building a simple dashboard, you don't want to import a massive 5MB library just to convert SOL to USDC or fetch a token's current price. &lt;/p&gt;

&lt;p&gt;To speed up my own workflow, I built a suite of zero-dependency, browser-native crypto utilities. No API keys required, no JWT tokens, just instant client-side lookups.&lt;/p&gt;

&lt;p&gt;Here are the three I use the most:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Instant Token Price Lookups
&lt;/h3&gt;

&lt;p&gt;Need to display the live price of ETH, BTC, or an altcoin in your React state? I got tired of reading CoinGecko documentation, so I made a &lt;a href="https://solvebar.com/tools/crypto-watchlist" rel="noopener noreferrer"&gt;crypto price checker&lt;/a&gt; where you just type the ticker (e.g., "PEPE"). It fetches the price, market cap, and 24h change instantly. &lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Unit Converter We Actually Need
&lt;/h3&gt;

&lt;p&gt;If you've ever tried to manually calculate how many Gwei equals 1 ETH, or how many Lamports are in a 5 SOL transaction, you know the math is annoying. I built a &lt;a href="https://solvebar.com/tools/eth-gas-tracker" rel="noopener noreferrer"&gt;crypto unit converter&lt;/a&gt; that handles the specific denominations for Bitcoin (BTC/Sats), Ethereum (ETH/Gwei/Wei), and Solana (SOL/Lamports).&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Gas Estimator for Quick Math
&lt;/h3&gt;

&lt;p&gt;Before sending a transaction on an EVM chain, I usually want a rough estimate of the fiat cost. Instead of switching tabs to a block explorer, I use this &lt;a href="https://solvebar.com/tools/eth-gas-tracker" rel="noopener noreferrer"&gt;crypto gas fee estimator&lt;/a&gt;. You plug in the gas limit and Gwei, and it tells you exactly what you'll pay in USD.&lt;/p&gt;

&lt;p&gt;All of these just wrap standard REST APIs but format the output specifically for developers. No fluff, no ads, just the data. &lt;/p&gt;

&lt;p&gt;What's your most annoying repetitive task when building crypto dashboards?&lt;/p&gt;

</description>
      <category>web3</category>
      <category>javascript</category>
      <category>cryptocurrency</category>
      <category>private</category>
    </item>
    <item>
      <title>How I cut my Next.js image payload by 60% without changing a line of code</title>
      <dc:creator>Shakeel Skl</dc:creator>
      <pubDate>Sat, 18 Apr 2026 14:44:30 +0000</pubDate>
      <link>https://dev.to/shakeel_skl_019683a0a1836/how-i-cut-my-nextjs-image-payload-by-60-without-changing-a-line-of-code-2mhd</link>
      <guid>https://dev.to/shakeel_skl_019683a0a1836/how-i-cut-my-nextjs-image-payload-by-60-without-changing-a-line-of-code-2mhd</guid>
      <description>&lt;p&gt;I was doing a routine Lighthouse audit on a Next.js app I'm building and realized my LCP (Largest Contentful Paint) was terrible. The culprit? Unoptimized hero images and Open Graph meta images.&lt;/p&gt;

&lt;p&gt;I didn't want to set up a heavy Sharp pipeline in my CI/CD just for a few static assets. I needed a quick way to optimize them before committing. &lt;/p&gt;

&lt;p&gt;Here is the exact 3-step workflow I used to fix it:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Convert formats to WebP
&lt;/h3&gt;

&lt;p&gt;Browsers love WebP. I had a few legacy PNGs that were huge. Instead of opening Photoshop, I dragged them into this &lt;a href="https://solvebar.com/tools/image-converter/png-to-webp" rel="noopener noreferrer"&gt;PNG to WebP converter&lt;/a&gt;. It runs 100% in the browser using the Canvas API, so you don't have to upload your assets to a random server.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Compress the file size
&lt;/h3&gt;

&lt;p&gt;Even some of my existing JPGs were too bloated. I ran them through this &lt;a href="https://solvebar.com/tools/image-compressor" rel="noopener noreferrer"&gt;bulk image compressor&lt;/a&gt;. You can just drag and drop 10 files at once, and it spits out optimized versions alongside a data table showing exactly how much KB you saved per file.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Fix layout shift with exact dimensions
&lt;/h3&gt;

&lt;p&gt;I was getting hit with CLS (Cumulative Layout Shift) penalties because my images didn't have explicit width and height props in my &lt;code&gt;&amp;lt;Image /&amp;gt;&lt;/code&gt; tags. I used this &lt;a href="https://solvebar.com/tools/image-resizer" rel="noopener noreferrer"&gt;image resizer&lt;/a&gt; to batch-crop all my blog headers to exactly 1200x630, eliminating the shift.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The result:&lt;/strong&gt; &lt;br&gt;
Lighthouse mobile score went from 54 to 94. No new npm packages installed. No server uploads. Just browser-native APIs. &lt;/p&gt;

&lt;p&gt;What's your go-to workflow for handling images before deploying?&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>nextjs</category>
      <category>webdev</category>
      <category>performance</category>
      <category>imageresizer</category>
    </item>
    <item>
      <title>5 PDF Tools That Never Upload Your Files</title>
      <dc:creator>Shakeel Skl</dc:creator>
      <pubDate>Mon, 30 Mar 2026 20:38:34 +0000</pubDate>
      <link>https://dev.to/shakeel_skl_019683a0a1836/5-pdf-tools-that-never-upload-your-files-4fl6</link>
      <guid>https://dev.to/shakeel_skl_019683a0a1836/5-pdf-tools-that-never-upload-your-files-4fl6</guid>
      <description>&lt;p&gt;We've all been there. You need to merge two PDFs quickly, so you Google "merge PDF free", click the first result, upload your documents — and then realize you just sent potentially sensitive files to some random server in another country.&lt;/p&gt;

&lt;p&gt;For most people, that's fine. But what if those PDFs contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A signed contract with client details&lt;/li&gt;
&lt;li&gt;A bank statement&lt;/li&gt;
&lt;li&gt;A confidential business proposal&lt;/li&gt;
&lt;li&gt;Medical records&lt;/li&gt;
&lt;li&gt;Legal documents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Suddenly that "free" tool doesn't feel so free.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem With Most Free PDF Tools
&lt;/h2&gt;

&lt;p&gt;Most online PDF tools work the same way:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You upload your file to their server&lt;/li&gt;
&lt;li&gt;Their server processes it&lt;/li&gt;
&lt;li&gt;You download the result&lt;/li&gt;
&lt;li&gt;Your file sits on their server &lt;em&gt;(for how long? who knows)&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Some tools are transparent about this. Many aren't. And even the honest ones — you're trusting their security, their data retention policies, and that they won't sell or misuse your data.&lt;/p&gt;

&lt;p&gt;There's a better way.&lt;/p&gt;




&lt;h2&gt;
  
  
  Browser-Based PDF Processing — How It Works
&lt;/h2&gt;

&lt;p&gt;Modern browsers are powerful enough to process PDF files entirely locally using JavaScript libraries like &lt;strong&gt;PDF.js&lt;/strong&gt; and &lt;strong&gt;pdf-lib&lt;/strong&gt;. This means a well-built tool can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read your PDF file in memory&lt;/li&gt;
&lt;li&gt;Process it (merge, split, compress, whatever)&lt;/li&gt;
&lt;li&gt;Give you the result to download&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All without your file ever touching a server.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Your file goes: device → browser → device. That's it.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  5 PDF Tools That Work This Way
&lt;/h2&gt;

&lt;p&gt;All of these tools are available free at &lt;strong&gt;&lt;a href="https://solvebar.com" rel="noopener noreferrer"&gt;solvebar.com&lt;/a&gt;&lt;/strong&gt; — no login, no upload, no tracking.&lt;/p&gt;




&lt;ol&gt;
&lt;li&gt;PDF Merger
Combine multiple PDF files into one. Drag to reorder before merging. The entire operation runs in your browser — useful when you need to combine contracts, reports or scanned documents without exposing their contents.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you need it: Combining a cover letter and portfolio into one file before sending a job application. Merging monthly bank statements into one annual document.&lt;/p&gt;

&lt;p&gt;👉 Merge your PDFs securely right now&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;PDF Splitter
Extract specific pages or split a PDF into individual pages. Set custom page ranges. Download split files individually or all at once as a ZIP.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you need it: A 50-page contract where you only need to share pages 12-15 with a client. Extracting one invoice from a batch PDF.&lt;/p&gt;

&lt;p&gt;👉 Extract pages from your PDF safely&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;PDF Compressor
Reduce PDF file size by removing embedded metadata and optimizing object streams. No quality slider that secretly re-renders your document through a server.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you need it: A scanned document that's 15MB and needs to be emailed. Reducing file size before uploading to a portal with size limits.&lt;/p&gt;

&lt;p&gt;👉 Compress your PDF without uploading it&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;PDF Watermark
Add custom text watermarks to all pages — control font, size, color, opacity and rotation. Mark documents as DRAFT, CONFIDENTIAL or SAMPLE before sharing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you need it: Sending a proposal to a client before the contract is signed. Sharing a sample report while protecting the full version.&lt;/p&gt;

&lt;p&gt;👉 Add a watermark to your PDF locally&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;PDF Form Filler
Fill in PDF form fields directly in your browser — text fields, checkboxes, radio buttons and dropdowns. Download the completed form as PDF.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you need it: Government forms, tax documents, application forms. These almost always contain personal information you really don't want sitting on a stranger's server.&lt;/p&gt;

&lt;p&gt;👉 Fill out your PDF forms privately&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The next time you need to process a PDF, ask yourself one question before uploading: &lt;em&gt;do I know where this file is going?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If the answer is no — or if the document contains anything sensitive — use a tool that keeps your files on your device.&lt;/p&gt;

&lt;p&gt;All five tools above are free, require no login, and process everything locally in your browser.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://solvebar.com/tools/pdf-merger" rel="noopener noreferrer"&gt;solvebar.com/tools/pdf-merger&lt;/a&gt;&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://solvebar.com/tools/pdf-splitter" rel="noopener noreferrer"&gt;solvebar.com/tools/pdf-splitter&lt;/a&gt;&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://solvebar.com/tools/pdf-compressor" rel="noopener noreferrer"&gt;solvebar.com/tools/pdf-compressor&lt;/a&gt;&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://solvebar.com/tools/pdf-watermark" rel="noopener noreferrer"&gt;solvebar.com/tools/pdf-watermark&lt;/a&gt;&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://solvebar.com/tools/pdf-form-filler" rel="noopener noreferrer"&gt;solvebar.com/tools/pdf-form-filler&lt;/a&gt;  &lt;/p&gt;




&lt;p&gt;&lt;em&gt;All tools on SolveBar run entirely in your browser. No files are uploaded, no data is tracked, no login is required.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>privacy</category>
      <category>webdev</category>
      <category>freetools</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Building browser-only PDF tools — merge, split, and canvas builder without touching a server</title>
      <dc:creator>Shakeel Skl</dc:creator>
      <pubDate>Mon, 23 Mar 2026 10:28:22 +0000</pubDate>
      <link>https://dev.to/shakeel_skl_019683a0a1836/building-browser-only-pdf-tools-merge-split-and-canvas-builder-without-touching-a-server-2517</link>
      <guid>https://dev.to/shakeel_skl_019683a0a1836/building-browser-only-pdf-tools-merge-split-and-canvas-builder-without-touching-a-server-2517</guid>
      <description>&lt;p&gt;Most PDF processing tools have a fundamental architecture problem: they upload your files to a server to do the work.&lt;/p&gt;

&lt;p&gt;This makes sense historically — PDF manipulation used to require server-side processing. But modern browser APIs have made that unnecessary for the majority of document workflows. pdf-lib, pdfjs-dist, and Fabric.js give you everything you need to merge, split, and build PDFs entirely client-side.&lt;/p&gt;

&lt;p&gt;Here's how I built three PDF tools without a single file upload.&lt;/p&gt;

&lt;h2&gt;
  
  
  The core libraries
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;pdf-lib&lt;/strong&gt; — JavaScript PDF creation and modification, runs in browser and Node. Handles merging, splitting, page manipulation, metadata. The API is clean:&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;PDFDocument&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="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="c1"&gt;// Merge&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;merged&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;src&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;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bytes&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;indices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseRanges&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;ranges&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPageCount&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;copied&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;merged&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;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;copied&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;merged&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bytes&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;merged&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;pdfjs-dist&lt;/strong&gt; — Mozilla's PDF renderer, runs in browser. Used for generating page thumbnails:&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;pdfjsLib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="nx"&gt;pdfjsLib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GlobalWorkerOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workerSrc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="s2"&gt;`https://cdnjs.cloudflare.com/ajax/libs/pdf.js/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pdfjsLib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/pdf.worker.min.mjs`&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="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&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;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="mi"&gt;1&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;0.3&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;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="s2"&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="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;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="s2"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;thumb&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="s2"&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="mf"&gt;0.7&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;jszip&lt;/strong&gt; — For packaging multiple split PDFs into a single ZIP download:&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;JSZip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jszip&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="k"&gt;default&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;zip&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JSZip&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;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;pageCount&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;doc&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="kd"&gt;const&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="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;doc&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;src&lt;/span&gt;&lt;span class="p"&gt;,&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="nx"&gt;doc&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bytes&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;doc&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="nx"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`page-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nc"&gt;String&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;padStart&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;.pdf`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bytes&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;zipBlob&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;zip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateAsync&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blob&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  PDF Merger — page range selection
&lt;/h2&gt;

&lt;p&gt;The interesting part of the merger is per-file page range selection. Users can specify "1-3,5,7-9" to select specific pages from each file before merging.&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;parseRanges&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pageCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;number&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;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pageCount&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&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="o"&gt;=&amp;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;pages&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;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;number&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;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&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;part&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;parts&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;part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Number&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="nx"&gt;a&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="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;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pageCount&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="nx"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&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;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;part&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;pageCount&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;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&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="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="nx"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&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;With live validation and a page count display so users know exactly what will be included.&lt;/p&gt;

&lt;h2&gt;
  
  
  PDF Splitter — three modes
&lt;/h2&gt;

&lt;p&gt;Rather than one split mode, I built three:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;All pages individually&lt;/strong&gt; — splits every page into a separate PDF, packages as ZIP. Simple, covers the most common use case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom ranges&lt;/strong&gt; — named ranges with labels. User creates entries like "Introduction: 1-3", "Chapter 1: 4-15", "Appendix: 16-20". Each becomes a separate PDF. Multiple ranges download as ZIP, single range downloads directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Visual page selection&lt;/strong&gt; — click thumbnails to select specific pages, downloads as single extracted PDF. Most intuitive for non-technical users.&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="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;SplitMode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;all&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ranges&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;select&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  PDF Canvas Builder — the contextual sidebar problem
&lt;/h2&gt;

&lt;p&gt;The canvas builder uses Fabric.js. The interesting UX challenge was the property sidebar: showing all properties all the time creates visual noise and confuses users about what applies to what.&lt;/p&gt;

&lt;p&gt;The solution was a contextual sidebar that shows only properties relevant to the selected object type:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Text selected&lt;/strong&gt; → font, size, color, weight, style, alignment, line height, rotation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shape selected&lt;/strong&gt; → fill, stroke, stroke width, opacity, rotation, flip H/V&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Image selected&lt;/strong&gt; → opacity, brightness, contrast, saturation, grayscale, rotation, flip H/V&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nothing selected&lt;/strong&gt; → hint card + add new object panels
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ObjType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shape&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&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;getObjType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ObjType&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;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;i-text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;itext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;textbox&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&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;t&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image&lt;/span&gt;&lt;span class="dl"&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;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shape&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&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 sidebar re-renders based on &lt;code&gt;selObj.type&lt;/code&gt;, showing the right controls for whatever is selected. Image filters (brightness/contrast/saturation/grayscale) use Fabric's built-in filter pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  The SSR problem with canvas-heavy components
&lt;/h2&gt;

&lt;p&gt;Next.js SSR and Fabric.js don't mix. The solution is dynamic import with &lt;code&gt;ssr: false&lt;/code&gt;:&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;// pages/tools/pdf-builder.tsx&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PdfBuilderClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/components/pdf/PdfBuilderClient&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;ssr&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same pattern for the merger/splitter — any component that touches &lt;code&gt;window&lt;/code&gt;, &lt;code&gt;document&lt;/code&gt;, or browser-only libraries needs this treatment in Next.js pages router.&lt;/p&gt;

&lt;h2&gt;
  
  
  Privacy by architecture
&lt;/h2&gt;

&lt;p&gt;The key insight: when you don't build a server, you can't accidentally leak data to it. The privacy guarantee isn't a policy — it's an architectural constraint.&lt;/p&gt;

&lt;p&gt;For PDF tools specifically this matters. Documents people merge and split often contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contracts and NDAs&lt;/li&gt;
&lt;li&gt;Financial statements&lt;/li&gt;
&lt;li&gt;Medical records&lt;/li&gt;
&lt;li&gt;Personal identification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of those should be uploaded to a third party service just because someone needs to combine two PDFs.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://solvebar.com/tools/pdf-merger" rel="noopener noreferrer"&gt;PDF Merger&lt;/a&gt;&lt;/strong&gt; — combine PDFs with page range selection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://solvebar.com/tools/pdf-splitter" rel="noopener noreferrer"&gt;PDF Splitter&lt;/a&gt;&lt;/strong&gt; — split by pages, ranges, or visual selection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://solvebar.com/tools/pdf-builder" rel="noopener noreferrer"&gt;PDF Canvas Builder&lt;/a&gt;&lt;/strong&gt; — create PDFs from scratch&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Open DevTools → Network tab while using any of them. No file upload requests. Everything stays in your browser.&lt;/p&gt;




&lt;p&gt;If you're building browser-based file processing tools, the pdf-lib + pdfjs-dist combination covers most PDF use cases without any server infrastructure. Worth knowing about.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>privacy</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
