<?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: Rick M</title>
    <description>The latest articles on DEV Community by Rick M (@rickkm).</description>
    <link>https://dev.to/rickkm</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%2F4065380%2F078e51b3-f54c-4661-bb93-8f34cd0ce381.png</url>
      <title>DEV Community: Rick M</title>
      <link>https://dev.to/rickkm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rickkm"/>
    <language>en</language>
    <item>
      <title>Why Local-First Web Apps are the Future: Building a Zero-Server PDF Engine with WebAssembly</title>
      <dc:creator>Rick M</dc:creator>
      <pubDate>Thu, 06 Aug 2026 09:02:33 +0000</pubDate>
      <link>https://dev.to/rickkm/why-local-first-web-apps-are-the-future-building-a-zero-server-pdf-engine-with-webassembly-43b</link>
      <guid>https://dev.to/rickkm/why-local-first-web-apps-are-the-future-building-a-zero-server-pdf-engine-with-webassembly-43b</guid>
      <description>&lt;p&gt;Modern web applications are changing.&lt;/p&gt;

&lt;p&gt;A few years ago, building a document editor, image compressor, or PDF toolkit almost always meant uploading files to a backend server, processing them remotely, and sending the results back to the user.&lt;/p&gt;

&lt;p&gt;Today, modern browsers are powerful enough to perform many of those same tasks locally.&lt;/p&gt;

&lt;p&gt;When building Fillora PDF, we re-evaluated our architecture and discovered that many common PDF operations didn't actually require a server. By moving supported workflows directly into the browser, we reduced unnecessary network requests while improving performance and privacy.&lt;/p&gt;

&lt;p&gt;In this article, I'll explain what &lt;strong&gt;local-first&lt;/strong&gt; means, the browser technologies that make it possible, and the engineering trade-offs we encountered along the way.&lt;/p&gt;




&lt;h1&gt;
  
  
  🏗️ What Does "Local-First" Mean?
&lt;/h1&gt;

&lt;p&gt;A &lt;strong&gt;local-first&lt;/strong&gt; application performs as much work as possible on the user's device before relying on cloud infrastructure.&lt;/p&gt;

&lt;p&gt;Instead of treating the browser as a thin client, it becomes the primary execution environment.&lt;/p&gt;

&lt;p&gt;For document applications, that means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📄 Opening files locally&lt;/li&gt;
&lt;li&gt;✏️ Editing documents in memory&lt;/li&gt;
&lt;li&gt;📥 Generating downloads without uploads&lt;/li&gt;
&lt;li&gt;☁️ Using the server only when it provides real value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Local-first doesn't mean eliminating servers completely.&lt;/p&gt;

&lt;p&gt;Instead, it means choosing the right place to execute each workload.&lt;/p&gt;




&lt;h1&gt;
  
  
  🌐 Traditional Server-Centric Architecture
&lt;/h1&gt;

&lt;p&gt;Many online document tools still follow this workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser
    │
    ├── Upload File
    ▼
Cloud Server
    │
    ├── Process Document
    ▼
Browser
    └── Download Result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although straightforward, this architecture introduces several challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upload latency for large files&lt;/li&gt;
&lt;li&gt;Higher bandwidth usage&lt;/li&gt;
&lt;li&gt;Increased server CPU and memory consumption&lt;/li&gt;
&lt;li&gt;Processing queues during peak traffic&lt;/li&gt;
&lt;li&gt;Privacy concerns when handling sensitive documents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For many PDF editing operations, these round trips simply aren't necessary.&lt;/p&gt;




&lt;h1&gt;
  
  
  ⚡ Browser-Native Architecture
&lt;/h1&gt;

&lt;p&gt;Modern browsers already provide the APIs needed to process many documents locally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser
    │
    ├── Read Local File
    ├── Process in Memory
    ├── Generate Output
    └── Download Result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For supported workflows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Documents remain on the user's device&lt;/li&gt;
&lt;li&gt;✅ No upload is required&lt;/li&gt;
&lt;li&gt;✅ Server CPU isn't consumed&lt;/li&gt;
&lt;li&gt;✅ Users receive results without waiting for upload and download cycles&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  🧩 Browser APIs That Make It Possible
&lt;/h1&gt;

&lt;p&gt;The browser has evolved into a capable application runtime.&lt;/p&gt;

&lt;p&gt;Several Web APIs make local-first document processing practical.&lt;/p&gt;




&lt;h2&gt;
  
  
  📂 FileReader API
&lt;/h2&gt;

&lt;p&gt;The FileReader API allows applications to read files directly from a user's device.&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;reader&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;FileReader&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readAsArrayBuffer&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Image previews&lt;/li&gt;
&lt;li&gt;PDF editors&lt;/li&gt;
&lt;li&gt;CSV importers&lt;/li&gt;
&lt;li&gt;Markdown editors&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📦 ArrayBuffer &amp;amp; Uint8Array
&lt;/h2&gt;

&lt;p&gt;PDFs, images, and many other file formats are fundamentally binary data.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ArrayBuffer&lt;/code&gt; and &lt;code&gt;Uint8Array&lt;/code&gt; allow JavaScript to manipulate those bytes efficiently without converting everything into strings.&lt;/p&gt;

&lt;p&gt;These typed arrays are the foundation of many browser-native document tools.&lt;/p&gt;




&lt;h2&gt;
  
  
  💾 Blob &amp;amp; URL.createObjectURL()
&lt;/h2&gt;

&lt;p&gt;After generating a new file, browsers can immediately create a downloadable object.&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="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;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;download&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;merged.pdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nf"&gt;setTimeout&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;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;revokeObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Common use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PDF generation&lt;/li&gt;
&lt;li&gt;CSV exports&lt;/li&gt;
&lt;li&gt;Image editors&lt;/li&gt;
&lt;li&gt;ZIP downloads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of uploading generated files back to a server, everything happens locally.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚙️ Web Workers
&lt;/h2&gt;

&lt;p&gt;Heavy JavaScript running on the main thread can freeze the user interface.&lt;/p&gt;

&lt;p&gt;Web Workers move expensive computations into a background thread.&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;worker&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;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;worker.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&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;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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;They're particularly useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Large PDF processing&lt;/li&gt;
&lt;li&gt;Image manipulation&lt;/li&gt;
&lt;li&gt;OCR&lt;/li&gt;
&lt;li&gt;Data parsing&lt;/li&gt;
&lt;li&gt;Compression&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keeping long-running tasks off the main thread makes applications feel significantly more responsive.&lt;/p&gt;




&lt;h2&gt;
  
  
  📄 JavaScript PDF Libraries
&lt;/h2&gt;

&lt;p&gt;Libraries like &lt;code&gt;pdf-lib&lt;/code&gt; make browser-native PDF editing remarkably straightforward.&lt;/p&gt;

&lt;p&gt;A simplified merge example looks like this:&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="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="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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;mergePdfFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffers&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;buffer&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;buffers&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;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;buffer&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="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="p"&gt;)&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;For many everyday PDF operations, the entire workflow completes without requiring the document to leave the user's device.&lt;/p&gt;




&lt;h1&gt;
  
  
  🧠 Where WebAssembly Fits In
&lt;/h1&gt;

&lt;p&gt;JavaScript is incredibly capable, but some workloads demand even more performance.&lt;/p&gt;

&lt;p&gt;WebAssembly (Wasm) enables code compiled from languages like C, C++, or Rust to run securely inside the browser at near-native speed.&lt;/p&gt;

&lt;p&gt;It's particularly valuable for computationally intensive tasks such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OCR engines&lt;/li&gt;
&lt;li&gt;Image processing&lt;/li&gt;
&lt;li&gt;Compression algorithms&lt;/li&gt;
&lt;li&gt;Advanced rendering&lt;/li&gt;
&lt;li&gt;Computer vision&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rather than replacing JavaScript, WebAssembly complements it by accelerating performance-critical components while JavaScript orchestrates the overall application.&lt;/p&gt;




&lt;h1&gt;
  
  
  ⚖️ Engineering Trade-Offs
&lt;/h1&gt;

&lt;p&gt;Local-first architecture isn't a silver bullet.&lt;/p&gt;

&lt;p&gt;Several practical limitations remain.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Browser Memory Limits
&lt;/h2&gt;

&lt;p&gt;Browsers enforce memory limits for each tab.&lt;/p&gt;

&lt;p&gt;Very large PDFs or thousands of pages can exceed available memory, particularly on mobile devices.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Complex Document Formats
&lt;/h2&gt;

&lt;p&gt;High-fidelity Office document rendering often depends on sophisticated rendering engines that are still better suited to dedicated backend services.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Bundle Size
&lt;/h2&gt;

&lt;p&gt;Shipping every processing engine to every visitor would dramatically increase initial page load times.&lt;/p&gt;

&lt;p&gt;Code splitting and lazy loading are essential for maintaining performance.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Hybrid Architectures Still Matter
&lt;/h2&gt;

&lt;p&gt;Not every workload belongs in the browser.&lt;/p&gt;

&lt;p&gt;A practical architecture combines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browser-native processing for supported editing tasks&lt;/li&gt;
&lt;li&gt;Server-side processing for workloads that genuinely require backend compute&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choosing the right execution environment is often more important than forcing everything onto one side.&lt;/p&gt;




&lt;h1&gt;
  
  
  📈 What Changed
&lt;/h1&gt;

&lt;p&gt;Moving supported PDF workflows into the browser produced several practical improvements.&lt;/p&gt;

&lt;p&gt;✅ Reduced infrastructure costs&lt;/p&gt;

&lt;p&gt;✅ Lower bandwidth consumption&lt;/p&gt;

&lt;p&gt;✅ Faster perceived performance&lt;/p&gt;

&lt;p&gt;✅ Better privacy for supported workflows&lt;/p&gt;

&lt;p&gt;✅ Improved scalability by reducing dependency on server compute&lt;/p&gt;

&lt;p&gt;Most importantly, backend resources became available for workloads that genuinely benefit from server-side processing.&lt;/p&gt;




&lt;h1&gt;
  
  
  💡 Lessons Learned
&lt;/h1&gt;

&lt;p&gt;The browser has evolved far beyond rendering HTML.&lt;/p&gt;

&lt;p&gt;Modern Web APIs, JavaScript, Web Workers, and WebAssembly make it possible to build applications that once required dedicated backend infrastructure.&lt;/p&gt;

&lt;p&gt;Local-first architecture isn't about eliminating the cloud.&lt;/p&gt;

&lt;p&gt;It's about using cloud resources intentionally while allowing capable client devices to perform the work they're already equipped to handle.&lt;/p&gt;




&lt;h1&gt;
  
  
  🎯 Final Thoughts
&lt;/h1&gt;

&lt;p&gt;The future of many web applications isn't about moving everything to the server.&lt;/p&gt;

&lt;p&gt;It's about moving the right workloads to the client.&lt;/p&gt;

&lt;p&gt;For us, adopting a local-first approach led to faster interactions, improved privacy for supported workflows, and a more scalable architecture while still relying on server-side processing where it provides clear benefits.&lt;/p&gt;

&lt;p&gt;As browsers continue to evolve, the line between desktop software and web applications will only become thinner.&lt;/p&gt;

&lt;p&gt;If you're building document tools, editors, design software, or productivity applications, it's worth asking one simple question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does this operation really need to leave the user's device?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'd love to hear how you're using browser APIs, WebAssembly, or local-first architecture in your own projects.&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>webassembly</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>architecture</category>
    </item>
    <item>
      <title>5 Browser APIs Every Web Developer Should Know (But Probably Aren't Using Enough)</title>
      <dc:creator>Rick M</dc:creator>
      <pubDate>Thu, 06 Aug 2026 08:22:27 +0000</pubDate>
      <link>https://dev.to/rickkm/5-browser-apis-every-web-developer-should-know-but-probably-arent-using-enough-3i8b</link>
      <guid>https://dev.to/rickkm/5-browser-apis-every-web-developer-should-know-but-probably-arent-using-enough-3i8b</guid>
      <description>&lt;h1&gt;
  
  
  🚀 5 Browser APIs Every Web Developer Should Know (But Probably Aren't Using Enough)
&lt;/h1&gt;

&lt;p&gt;Modern browsers have become incredibly powerful application platforms.&lt;/p&gt;

&lt;p&gt;Features that once required backend services or heavy third-party libraries can now run directly in the browser using built-in Web APIs.&lt;/p&gt;

&lt;p&gt;Whether you're building dashboards, productivity tools, editors, AI applications, or document software, these APIs can help you build faster, more responsive, and more privacy-friendly experiences.&lt;/p&gt;

&lt;p&gt;Let's explore five browser APIs that every frontend developer should have in their toolkit.&lt;/p&gt;




&lt;h2&gt;
  
  
  📋 Quick Overview
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;API&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;th&gt;Why It Matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;📂 FileReader&lt;/td&gt;
&lt;td&gt;Reading local files&lt;/td&gt;
&lt;td&gt;No uploads required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;💾 URL.createObjectURL()&lt;/td&gt;
&lt;td&gt;Downloading generated files&lt;/td&gt;
&lt;td&gt;Faster file downloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;⚙️ Web Workers&lt;/td&gt;
&lt;td&gt;Heavy computations&lt;/td&gt;
&lt;td&gt;Keeps the UI responsive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📋 Clipboard API&lt;/td&gt;
&lt;td&gt;Copying text&lt;/td&gt;
&lt;td&gt;Modern replacement for &lt;code&gt;execCommand()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👀 Intersection Observer&lt;/td&gt;
&lt;td&gt;Detecting visibility&lt;/td&gt;
&lt;td&gt;Better than scroll listeners&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h1&gt;
  
  
  📂 1. FileReader API
&lt;/h1&gt;

&lt;p&gt;Need users to open local files without uploading them first?&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;FileReader API&lt;/strong&gt; lets your application read files directly from the user's device.&lt;/p&gt;

&lt;p&gt;This is one of the easiest ways to improve both privacy and user experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Perfect for
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🖼️ Image previews&lt;/li&gt;
&lt;li&gt;📊 CSV importers&lt;/li&gt;
&lt;li&gt;📄 PDF viewers&lt;/li&gt;
&lt;li&gt;📝 Markdown editors&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&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;reader&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;FileReader&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readAsArrayBuffer&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Why it matters&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Users can immediately work with their files without waiting for uploads or worrying about sensitive documents leaving their device.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  💾 2. URL.createObjectURL()
&lt;/h1&gt;

&lt;p&gt;Generating a PDF, CSV, ZIP, or image?&lt;/p&gt;

&lt;p&gt;Instead of uploading the file to a backend just to download it again, you can create a downloadable file directly in memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="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;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;download&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;document.pdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;revokeObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Great for
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;📄 PDF generators&lt;/li&gt;
&lt;li&gt;📊 CSV exports&lt;/li&gt;
&lt;li&gt;🖼️ Image editors&lt;/li&gt;
&lt;li&gt;📦 ZIP downloads&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Why it matters&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Eliminating unnecessary uploads makes downloads feel much faster and reduces server bandwidth.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  ⚙️ 3. Web Workers
&lt;/h1&gt;

&lt;p&gt;Have you ever clicked a button and watched the entire page freeze?&lt;/p&gt;

&lt;p&gt;That's because JavaScript normally runs on the browser's main thread.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Web Workers&lt;/strong&gt; allow expensive tasks to run in the background without blocking the interface.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&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;worker&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;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;worker.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&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;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Great for
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🖼️ Image processing&lt;/li&gt;
&lt;li&gt;📊 Parsing large datasets&lt;/li&gt;
&lt;li&gt;🧮 Heavy calculations&lt;/li&gt;
&lt;li&gt;📄 Document processing&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Why it matters&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A responsive application feels dramatically faster—even when it's doing complex work behind the scenes.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  📋 4. Clipboard API
&lt;/h1&gt;

&lt;p&gt;Copying text used to rely on &lt;code&gt;document.execCommand()&lt;/code&gt;, which is now deprecated.&lt;/p&gt;

&lt;p&gt;The modern &lt;strong&gt;Clipboard API&lt;/strong&gt; makes copying text simple.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clipboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Copied!&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;h3&gt;
  
  
  Great for
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🔗 Share buttons&lt;/li&gt;
&lt;li&gt;💻 Copy code&lt;/li&gt;
&lt;li&gt;📋 Productivity apps&lt;/li&gt;
&lt;li&gt;📄 Generated text&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Why it matters&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cleaner code, better browser support, and a much nicer developer experience.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  👀 5. Intersection Observer API
&lt;/h1&gt;

&lt;p&gt;Listening to every scroll event can quickly become inefficient.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Intersection Observer API&lt;/strong&gt; tells you when an element enters or leaves the viewport.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&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;observer&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;IntersectionObserver&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;entries&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;entries&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;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isIntersecting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Visible&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="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&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;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.card&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;h3&gt;
  
  
  Great for
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;⚡ Lazy loading&lt;/li&gt;
&lt;li&gt;♾️ Infinite scrolling&lt;/li&gt;
&lt;li&gt;✨ Scroll animations&lt;/li&gt;
&lt;li&gt;📈 Visibility analytics&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Why it matters&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's far more efficient than constantly checking scroll position.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  🎯 Which API Should You Use?
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;If you're building...&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Image uploader&lt;/td&gt;
&lt;td&gt;FileReader&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDF generator&lt;/td&gt;
&lt;td&gt;URL.createObjectURL()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI application&lt;/td&gt;
&lt;td&gt;Web Workers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code editor&lt;/td&gt;
&lt;td&gt;Clipboard API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blog or portfolio&lt;/td&gt;
&lt;td&gt;Intersection Observer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h1&gt;
  
  
  🌟 Final Thoughts
&lt;/h1&gt;

&lt;p&gt;Modern browsers already provide many of the tools needed to build rich web applications.&lt;/p&gt;

&lt;p&gt;Learning these APIs can help you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🚀 Improve performance&lt;/li&gt;
&lt;li&gt;🔒 Enhance user privacy&lt;/li&gt;
&lt;li&gt;⚡ Reduce unnecessary backend requests&lt;/li&gt;
&lt;li&gt;📦 Depend less on external libraries&lt;/li&gt;
&lt;li&gt;😊 Deliver a smoother user experience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The best part?&lt;/p&gt;

&lt;p&gt;Most of these APIs have been supported by modern browsers for years—you can start using them today.&lt;/p&gt;




&lt;h2&gt;
  
  
  💬 What About You?
&lt;/h2&gt;

&lt;p&gt;Which browser API has had the biggest impact on your projects?&lt;/p&gt;

&lt;p&gt;Or is there another underrated Web API that every developer should know?&lt;/p&gt;

&lt;p&gt;I'd love to hear your recommendations in the comments!&lt;/p&gt;

</description>
      <category>api</category>
      <category>frontend</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Building Fillora PDF: Why We Moved Heavy PDF Processing to Client-Side WebAssembly</title>
      <dc:creator>Rick M</dc:creator>
      <pubDate>Thu, 06 Aug 2026 07:41:43 +0000</pubDate>
      <link>https://dev.to/rickkm/why-we-moved-heavy-pdf-processing-from-server-apis-to-100-client-side-webassembly-3l62</link>
      <guid>https://dev.to/rickkm/why-we-moved-heavy-pdf-processing-from-server-apis-to-100-client-side-webassembly-3l62</guid>
      <description>&lt;h1&gt;
  
  
  Building Fillora PDF: Moving PDF Processing from Server APIs to Browser-Native Execution
&lt;/h1&gt;

&lt;p&gt;Every developer who has built document conversion features has probably encountered the same challenges: large file uploads, server memory pressure, processing queues, timeout limits, and users who hesitate to upload sensitive documents to third-party servers.&lt;/p&gt;

&lt;p&gt;When building Fillora PDF, we redesigned many of our core PDF manipulation tools to run directly inside the browser instead of relying on server-side processing.&lt;/p&gt;

&lt;p&gt;This article explains why we made that architectural decision, how modern browser APIs make it practical, and the trade-offs we discovered along the way.&lt;/p&gt;




&lt;h2&gt;
  
  
  🏗️ Server Processing vs. Browser-Native Processing
&lt;/h2&gt;

&lt;p&gt;Traditional online PDF tools typically process every operation on a remote server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Traditional Server Workflow

Browser
   │
   ├── Upload PDF
   ▼
Server
   │
   ├── Process Document
   ▼
Browser
   └── Download Result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this approach works well, it introduces several challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upload latency for large files&lt;/li&gt;
&lt;li&gt;Higher server CPU and memory usage&lt;/li&gt;
&lt;li&gt;Increased bandwidth costs&lt;/li&gt;
&lt;li&gt;Privacy concerns when processing sensitive documents&lt;/li&gt;
&lt;li&gt;Scalability challenges during traffic spikes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For many PDF editing operations—such as merging, splitting, rotating, rearranging pages, and metadata editing—the browser is now capable of doing the work locally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser-Native Workflow

Browser
   │
   ├── Read Local File
   ├── Process Document
   └── Download Result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Documents do not need to be uploaded for supported browser-native operations.&lt;/li&gt;
&lt;li&gt;Processing happens on the user's device rather than consuming server CPU.&lt;/li&gt;
&lt;li&gt;Users receive immediate results without waiting for upload and download cycles.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚡ Why Modern Browsers Can Handle PDF Processing
&lt;/h2&gt;

&lt;p&gt;Modern browsers provide powerful APIs that make client-side document processing practical.&lt;/p&gt;

&lt;p&gt;Some of the key building blocks include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FileReader API&lt;/strong&gt; for reading local files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blob&lt;/strong&gt; and &lt;strong&gt;URL.createObjectURL()&lt;/strong&gt; for generating downloadable files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ArrayBuffer&lt;/strong&gt; and &lt;strong&gt;Uint8Array&lt;/strong&gt; for binary data manipulation&lt;/li&gt;
&lt;li&gt;JavaScript libraries such as &lt;strong&gt;pdf-lib&lt;/strong&gt; for PDF editing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web Workers&lt;/strong&gt; for moving heavy processing off the main UI thread&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simplified merge example looks like this:&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="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="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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;mergePdfFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffers&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;buffer&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;buffers&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;source&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;buffer&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;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;source&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="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="p"&gt;)&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;Because the entire operation runs locally, many everyday PDF tasks can complete quickly without requiring the document to leave the user's device.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧵 Keeping the Interface Responsive
&lt;/h2&gt;

&lt;p&gt;One challenge with client-side processing is preventing expensive operations from freezing the interface.&lt;/p&gt;

&lt;p&gt;Heavy computations performed on the main thread can block rendering and make the application feel unresponsive.&lt;/p&gt;

&lt;p&gt;To avoid this, long-running tasks can be executed inside &lt;strong&gt;Web Workers&lt;/strong&gt;, allowing the browser to continue rendering the interface while processing documents in the background.&lt;/p&gt;

&lt;p&gt;The result is a smoother experience, especially when working with larger files.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚖️ Trade-offs We Encountered
&lt;/h2&gt;

&lt;p&gt;Moving processing into the browser is not a universal solution.&lt;/p&gt;

&lt;p&gt;Several practical limitations remain.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Browser Memory Limits
&lt;/h3&gt;

&lt;p&gt;Browsers enforce memory limits per tab.&lt;/p&gt;

&lt;p&gt;Very large PDFs or documents containing thousands of pages can exhaust available memory, particularly on mobile devices.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Complex Document Formats
&lt;/h3&gt;

&lt;p&gt;Converting richly formatted Office documents into PDF often requires sophisticated rendering engines.&lt;/p&gt;

&lt;p&gt;These workloads are generally better suited to dedicated server-side services.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Bundle Size
&lt;/h3&gt;

&lt;p&gt;Adding every processing engine to the initial application bundle would significantly increase page load times.&lt;/p&gt;

&lt;p&gt;We therefore load only the functionality required for the current tool whenever possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Fallback Processing
&lt;/h3&gt;

&lt;p&gt;Some workloads simply exceed what browsers can comfortably handle.&lt;/p&gt;

&lt;p&gt;In those situations, server-side processing remains an important fallback.&lt;/p&gt;




&lt;h2&gt;
  
  
  📈 What Changed
&lt;/h2&gt;

&lt;p&gt;For browser-native PDF operations we observed several practical benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduced server infrastructure costs&lt;/li&gt;
&lt;li&gt;Lower bandwidth consumption&lt;/li&gt;
&lt;li&gt;Faster perceived performance by eliminating upload delays&lt;/li&gt;
&lt;li&gt;Improved privacy for supported workflows&lt;/li&gt;
&lt;li&gt;Better scalability because document processing no longer depends on server compute&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rather than replacing server processing entirely, we found that a hybrid architecture works best:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browser-native execution for supported PDF editing tasks&lt;/li&gt;
&lt;li&gt;Server-side processing only when the workload genuinely requires it&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💬 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Modern browsers have evolved into powerful application platforms.&lt;/p&gt;

&lt;p&gt;Features that once required dedicated backend infrastructure can now often be performed directly on the user's device using standard browser APIs and mature JavaScript libraries.&lt;/p&gt;

&lt;p&gt;For us, moving many PDF operations into the browser simplified our infrastructure while providing a faster and more privacy-friendly experience for users.&lt;/p&gt;

&lt;p&gt;If you've built browser-native document tools, I'd love to hear what approaches worked well for you.&lt;/p&gt;

&lt;p&gt;You can explore our implementation at &lt;strong&gt;&lt;a href="https://fillorapdf.com" rel="noopener noreferrer"&gt;https://fillorapdf.com&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>performance</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
