In modern web apps, especially in enterprise-level Angular projects, it’s easy to run into performance issues when handling large datasets, complex calculations, or background processing. One common problem? Your UI freezes because everything is running on the main thread.
That's where Web Workers come in.
In this post, I’ll show you how to use Web Workers in Angular to move heavy work off the main thread. We’ll walk through the concepts, how to implement them step by step, and I’ll share how I used this approach in a real project where I had to generate massive financial reports in the browser.
What Are Web Workers?
A Web Worker is a JavaScript feature that lets you run scripts in a background thread, separate from the browser's main execution thread. This is useful for:
- Heavy data processing
- Long-running loops
- Fetching large amounts of data
- Keeping the UI responsive
In Angular, Web Workers are fully supported and can be generated easily with CLI.
When Should You Use Web Workers?
Use Web Workers when:
- Your app freezes or becomes unresponsive during processing.
- You need to handle large datasets or long calculations.
- You want to do background processing (like prefetching or progressive rendering).
- You're working with browser limitations (e.g. timers stopping on inactive tabs).
In one of my previous projects, I had to print massive financial reports—thousands of rows with dynamic fields. The browser would freeze, the tab would stop processing if switched (Chrome behavior), and it broke the user experience. I solved this using Web Workers. More on that below!
Setting Up a Web Worker in Angular
1. Generate the Web Worker
ng generate web-worker report
This will create a file like src/app/report.worker.ts.
Angular handles the bundling and setup automatically using Webpack.
2. Write Worker Logic
Inside report.worker.ts
, write the logic you want to run in the background.
Here’s an example that progressively fetches paginated data from an API:
/// <reference lib="webworker" />
addEventListener('message', ({ data }) => {
const { apiUrl, totalPages, limit, interval } = data;
let currentPage = 0;
const fetchPage = async () => {
if (currentPage >= totalPages) {
postMessage({ done: true });
return;
}
try {
const response = await fetch(`${apiUrl}?offset=${currentPage * limit}&limit=${limit}`);
const pageData = await response.json();
postMessage({ data: pageData, page: currentPage });
} catch (error) {
postMessage({ error: error.message, page: currentPage });
}
currentPage++;
setTimeout(fetchPage, interval); // throttle requests
};
fetchPage();
});
3. Use the Worker in Your Angular Component
const worker = new Worker(new URL('./report.worker', import.meta.url), { type: 'module' });
worker.postMessage({
apiUrl: 'https://api.example.com/report',
totalPages: 50,
limit: 100,
interval: 300,
});
worker.onmessage = ({ data }) => {
if (data.done) {
console.log('All data received. Ready to render or print.');
// Trigger rendering or printing
} else if (data.error) {
console.error(`Error fetching page ${data.page}:`, data.error);
} else {
console.log(`Received page ${data.page}`, data.data);
this.rows.push(...data.data); // append to your table or buffer
}
};
Real-Life Example: Dynamic Report Generation Without Freezing
Here’s how I used this in practice:
- We had to generate printable financial reports for clients, often 10,000+ rows.
- The previous pixel-perfect engine required manual template design and choked on big data.
- I built a dynamic front-end reporting component that:
- Took column config and data API as input
- Fetched paginated data via a Web Worker
- Rendered the report in the DOM and used
window.print()
to export
Why Web Workers saved the day:
- The UI didn’t freeze
- Chrome tab switching no longer paused data generation
- DB performance was protected with rate-limited fetches
- No backend processing—everything happened client-side
Things to Keep in Mind
- Web Workers can’t access Angular services, DOM, or global window directly. You’ll use native
fetch
or minimal logic. - You need to serialize data between the worker and the main thread.
- Heavy workers can still use too much memory—consider chunking results.
Summary
Web Workers are an underused but powerful way to handle heavy work in Angular apps without hurting the user experience. Use them when your UI starts freezing or you need to keep processes alive in the background.
My advice: don’t wait for performance issues to show up. If you’re building something that could grow in size or complexity, offload early.
Let me know if you’ve used Web Workers before or want to share a performance war story. I’d love to hear it.