How we improved our client-side PDF generation by 5x
Karan Janthe
Posted on March 17, 2024
At Smartagent, 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:
Using react-pdf, 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:
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
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.
- Experiment 2: Web-worker
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.
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.
Posted on March 17, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.