How we improved our client-side PDF generation by 5x
Tags: webassembly, webworker, pdf, javascript
Published: 2024-03-17 00:43:06

At [Smartagent](https://online.smartagent.one), we faced a challenge that might sound familiar to many developers: our client-side PDF generation feature was struggling under the weight of massive user demands. Our customers love customization—they want to generate various reports, tweaking everything from formats to the data columns they see. To give you an idea, here's a glimpse into the level of customization we provide:

![list and pdf demo](https://cloud-motl7vpyq-hack-club-bot.vercel.app/0clientlist_pdf.gif)

Using [react-pdf](https://react-pdf.org/), we crafted a solution that allowed users to manipulate their reports with an impressive degree of flexibility. But, as data grew (imagine trying to cram an entire financial year's worth of invoices, up to 22,000 rows, into one PDF), our solution began to falter, especially on older PCs with limited resources.

The "Aw, Snap!" error became a common sight, frustrating users trying to generate their reports:

![aw snap](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/my48kiuroov0yynys0vj.png)

### The Quest for a Solution

Initially, we experimented with the idea of shifting PDF generation to the server side. However, this approach would require us to rewrite our entire PDF generation logic a daunting task given the time-sensitive nature of the issue.

So, we turned our attention back to the client side, aiming for a solution that could gracefully handle large datasets even on low-spec PCs.

### Diving into Experiments

Our journey led us to explore two promising technologies: WebAssembly (Wasm) and web workers.

- **Experiment 1: [Wasm-PDF](https://faker-pdf-wasm.vercel.app/)**

  We combined Wasm with Faker to create a demo for high-performance PDF generation. The results were eye-opening: generating a PDF with 1,000 rows took just 200ms, a task that previously took over a minute and often led to crashes. For 22,000 rows, the process took a mere 6 seconds.

  ![WASM PDF](https://cloud-5r02hw84n-hack-club-bot.vercel.app/0wasmpdf.gif)

- **Experiment 2: [Web-worker](https://react-pdf-webworker.vercel.app/)**

  We discovered that our application was crashing because PDF generation was blocking the main thread. By leveraging web workers, we could offload this task, allowing the app to remain responsive.

  With web workers, generating 1,000 rows took only 3 seconds.

  ![Web Worker](https://cloud-mmt51o2ig-hack-club-bot.vercel.app/0web-worker.gif)

### What We Learned

Our exploration revealed valuable insights:

- **Performance vs. Practicality**: While Wasm offered incredible speed, it required a significant time to rewrite our PDF logic. Given our tight schedule and the learning curve associated with Wasm and Rust, we had to consider alternative solutions.
  
- **The Power of Web Workers**: Web workers provided a practical way to improve performance without needing to rewrite our existing logic. By moving PDF generation off the main thread, we could prevent crashes and improve user experience, even if the process took a bit longer.

### Our Choice and Why

In the end, we opted for the web worker solution. It addressed our immediate needs without sacrificing the user experience. Adding a progress bar gave users visual feedback, reassuring them that their reports were on their way.

This decision was a compromise, shaped by our constraints and our commitment to delivering a reliable service. However, our exploration of Wasm opened up exciting possibilities for future optimizations.