DEV Community

Cover image for How we improved our client-side PDF generation by 5x
Karan Janthe
Karan Janthe

Posted on

How we improved our client-side PDF generation by 5x

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:

list and pdf demo

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:

aw snap

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.

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

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

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.

Top comments (17)

Collapse
 
auroratide profile image
Timothy Foster

One of my clients wanted PDF generation and we realized: literally just window.print() does this. Saved us probably months of development time and architectural heartache.

Collapse
 
xiaowo profile image
harry li

yes but sometime we need assembled data

Collapse
 
mordechaim profile image
Mordechai Meisels

@media print

Collapse
 
soanvig profile image
Mateusz Koteja • Edited

Wtf. Why you are generating PDFs on the frontend, not backend? Especially for 22k rows. I understand you send those rows to the client? My god.

"this approach would require us to rewrite our entire PDF generation logic a daunting task "

Yes, because you made a mistake in the beginning. Honestly you should get it straight ASAP. Those PDFs were generating for at least 30 seconds, and im sure you had a lot of time previously to tackle that problem.

The solution is cool though, anyway

Collapse
 
kvetoslavnovak profile image
kvetoslavnovak • Edited

From my own experience I can confirm that frontend client side web worker solution is the way to go.

Collapse
 
j0nimost profile image
John Nyingi

client side loading 22k rows, chances are high of rendering a users machine unusable while it's processing the PDF, isn't that going to affect user experiences?

Collapse
 
karanjanthe profile image
Karan Janthe

tanstack table handles this in very good way, it has virtualization and other UI related optimization and obviously, we use pagination!

Collapse
 
krishna404 profile image
krishna404

I believe @j0nimost is talking about pdf-creation & rendering.

Collapse
 
krishna404 profile image
krishna404

In a web-worker, it takes longer than the main thread but we didnt face that issue. Phones did heat up though...

Collapse
 
j0nimost profile image
John Nyingi

lol 😂, i can imagine it on older laptops 4th gen intel laptops with 4GB ram, or tablets.

Collapse
 
krishna404 profile image
krishna404

You talked about 22k rows. How much time did 22k rows take?

Collapse
 
shradhhu_53 profile image
Shraddha Khattar

It must have taken longer time.

Collapse
 
krishna404 profile image
krishna404

I thought 22 is a smaller number :|

Collapse
 
shifi profile image
Shifa Ur Rehman

Well ofcourse watson.

Collapse
 
monjai profile image
mon-jai • Edited

What wasm pdf library did you use?

Collapse
 
timothyokooboh profile image
timothyokooboh

Thanks for sharing!