Optimizing JavaScript can be a bit tricky sometimes, but you can analyze the JavaScripts that are being used. We can categorize the JavaScripts into multiple segments, such as:
Main thread JS
Critical JS – Typically JS that only affects above-the-fold elements of a page, such as a hamburger menu click event, or any other button click to show popup, etc.
Asynchronous JS – The JS that a page needs on the ready event or before the onload event.
Defer JS – The JS that can be loaded onload, that is, after all the page content and assets are downloaded.
Worker Thread JS – The JS that we can load lazily and run only on specific events or user interactions such as scroll, click, etc.
In some cases, we just need to defer JavaScript so that it loads only after the page is loaded. To understand which JS you need first and which ones you need later, you can find out how your page looks with Javascript disabled in the browser.
If your web page shows all page elements with CSS properly, then it means your page doesn’t need JavaScript to load on initial page rendering, and you can basically keep the main thread idle for other executions and interactions for users. This will reduce time to interact and reduce total blocking time.
Here is how a web page gets rendered. As you can see in the above diagram, when a page request is made:
The network sends a response with HTML, which might take less than 100ms based on the server performance.
After this DOM building process gets started. The CSS and JS files are requested; JavaScripts will be requested either asynchronously or synchronously. During this DOM building process, the main thread is blocked for any interactions.
If these CSS and Javascript requests are more in number, that will add to blocking time. All this happens on the main thread. Hence, we often get suggestions to reduce the main thread javascripts and eliminate render-blocking resources.
Once all CSS resources are collected and ready, the Build CSSOM process gets started. The time taken by this process will depend on the number of CSS files we have on the page. Inline CSS added on the page gets built really quickly as compared to the files requested using HTTP requests, i.e., normal CSS stylesheet links.
This document describes the DOM building process very nicely and why async or deferred JavaScript is important.
Strategy to load JavaScripts:
First of all, analyze and measure the number of JavaScripts.
Create a list of JavaScript files that absolutely need to load asynchronously during the initial page load and are required at the document.ready or DOMContentLoaded stage. These can be considered your “critical JavaScripts.” Only include scripts essential for above-the-fold functionality. Keep this list as small as possible, since these scripts run on the main thread and can keep it occupied. A busy main thread will negatively impact overall FID and TTI.
Make a list of JavaScript files that can safely be loaded after the rest of the page has finished loading. These can be categorized as “non-critical JavaScripts.” Place these scripts in the footer, after all textual and media content, and add the defer attribute so they load without blocking the initial render.
Make a list of JavaScripts that are only needed when users try to interact with the pages. Such JavaScripts can be offloaded to a worker thread so they don’t keep the main thread busy, and once the user tries to interact with the page by the means of a keypress, tap, click, or scroll, these JavaScripts can be sent back to the main thread and loaded normally.
If you’re using WordPress, you can handle this using our rt Scripts Optimizer plugin. It loads the JavaScripts marked with type="text/rtscript" on a worker thread, ensuring that the main thread executes only the essential scripts before the DOM finishes loading. When the user interacts with the page, the plugin then loads all remaining JavaScripts normally. In essence, this approach allows you to “lazy load” JavaScript—covering loading, parsing, and execution—without blocking the main thread during the initial render.
There’s another library that can help us achieve the offloading of JavaScripts to a worker thread using Party Town JS.