Marcio Cunha

Pages freezing? How to use Web Workers in React apps

Learn how to offload heavy data processing from the browser main thread using Web Workers to eliminate freezes and improve smoothness in React apps.

Marcio Cunha•4 min
Also available in:PortuguêsEspañol
Summary
  • The interface freezes when the main thread runs time-consuming calculations while trying to render visual elements at the same time.
  • Web Workers run background scripts in isolated browser threads, preventing heavy tasks from blocking user interaction.
  • Communication between the React component and the Web Worker happens exclusively through asynchronous messages using postMessage.
  • Modern libraries and custom hooks make it easy to integrate Web Workers without messing up the application architecture.
  • Real performance gains appear in scenarios such as processing large spreadsheets, cryptography, and intense data manipulation.

Why does the React interface freeze during heavy tasks?

When developing web applications, we tend to place all business logic, network requests, and state manipulation in the same structure that draws the screen. In the browser, the JavaScript engine runs natively in a single traffic lane called the main thread or single execution thread. In practice, this means that if your code decides to calculate complex statistics from a table with ten thousand rows, the browser must pause absolutely everything — including button animations or click responses — to finish that mathematical calculation. The visible result for anyone using the system is a page that feels stuck, frozen, or responding with an annoying delay.

In modern component-based ecosystems, such as slow apps using React Server Components efficiently, architecture helps delegate part of the computational effort to the server, but plenty of client-side processing load still remains. When the browser needs to filter massive arrays, compress files, decode heavy tokens, or render detailed charts right in the browser, the single available processing line gets choked. It is precisely in this critical scenario that developers must seek architectural alternatives to lift the weight off the shoulders of the visual interface.

What are Web Workers and how do they work in practice?

To solve the screen freezing problem without having to migrate the entire application to the cloud, modern browsers offer a native tool called Web Workers. Simply put, a Web Worker is a separate JavaScript file running in the background, completely isolated from the main thread where React mounts visual components. In practice, this works like opening a separate tab in your brain just to do hard math while you continue talking normally to someone in front of you without losing your train of thought.

Since this secondary script lives in an isolated universe, it cannot directly access the DOM, which is the page's tree of visual elements, nor the browser's global window object. This security and architectural restriction ensures that code running in the worker cannot unexpectedly break the application's visual state. All information exchange between your React component and the Web Worker happens through a messenger system based on messages, where you send data using the postMessage command and receive responses through event listeners known as onmessage.

How to create and integrate a Web Worker in a component

To get your hands dirty and remove heavy processing from the main thread, the first step is to create a dedicated JavaScript file for background work. Suppose we need to filter a massive volume of numerical records received from an API. We create a file called worker.js that listens to messages sent by the main application, executes the time-consuming calculation, and returns the ready-to-display result.

self.onmessage = function(e) {const rawData = e.data;const processedResult = rawData.filter(item => item.active);self.postMessage(processedResult);};

On the React component side, you initialize this worker using the browser's native worker constructor API and configure event listeners inside an effect hook to avoid unnecessary recreations on every render. This approach ensures that the interface remains clean, fluid, and fully responsive while heavy processing happens under the hood, keeping the user experience impeccable even under heavy data loads.

Challenges, trade-offs, and when it is actually worth using

Although Web Workers look like a miraculous silver bullet for any interface slowdown, it is crucial to understand the trade-offs involved before applying them everywhere in the application. Because data needs to be copied or transferred between the main thread and the worker thread through the messaging system, there is a small serialization cost. In practice, if you send a tiny object for the worker to process in one millisecond, the time spent packing and unpacking the message might end up being longer than the calculation itself.

Another important point is the maintenance complexity of the code. Debugging errors inside a separate file running in the background requires specific browser tools and adds an extra layer of abstraction to the project. Therefore, the golden rule in frontend engineering is clear: use Web Workers only for purely computational and time-consuming tasks that exceed the perceptible mark of sixteen milliseconds per animation frame. For standard API requests or simple state manipulations, keeping the default flow on the main thread remains the most sensible and clean choice. To explore these architectural concepts further in production, see our related technical analysis on Unstable AI? How to use RAG to anchor answers.

Final considerations on performance and smoothness in React

Ensuring that a web application remains fast and responsive under any circumstance is no longer a luxury and has become a basic usability requirement. Throughout this article, we saw how the browser main thread suffers when accumulating visual renders and heavy calculations at the same time, generating those annoying freezes on the screen. Understanding the limits of the JavaScript engine and learning to offload computational effort to Web Workers is a watershed moment in the career of any frontend developer looking to deliver professional and highly scalable systems.

Adopting good architecture and optimization practices not only benefits technical performance but directly transforms the end user's perception of quality regarding your product. By combining modern code splitting techniques, such as optimizations in heavy bundles using tree-shaking in Vite, with the smart use of background processing, you build robust applications capable of handling large volumes of data without ever losing interface smoothness.