The content was visible, the network request had finished, and the load report looked respectable. The main thread was simply too busy parsing, executing, and hydrating JavaScript to respond.
If you optimize only the first render, you may be measuring the moment the interface becomes visible rather than the moment it becomes usable.
Measure what happens after the user clicks, types, opens a menu, or submits a form. A fast screenshot does not guarantee a responsive application.
Begin with an action a user actually performs: opening the main navigation, typing into search, applying a filter, expanding a product panel, adding an item to a cart, submitting a form.
If there is a noticeable pause after the input, the page has a responsiveness problem even when initial content appeared quickly.
Interaction to Next Paint, or INP, measures the latency of user interactions across a page visit. Web performance guidance recommends an INP of 200 milliseconds or less at the 75th percentile for a good user experience.
Field data is the strongest evidence because it reflects real devices, networks, and interactions. Lab tools help reproduce and explain the problem.
Open browser DevTools, switch to the Performance panel, and record the interaction that feels slow.
During the recording: wait for the page to settle, click or type exactly as a user would, stop recording after the interface updates, inspect the main-thread timeline around the interaction.
A long task is an uninterrupted period where the main UI thread stays busy for at least 50 milliseconds. While that task runs, the browser cannot process another main-thread task, including input handling and rendering work.
Common causes include: expensive event handlers, parsing and evaluating large scripts, synchronous data processing, repeated rendering, forced layout, third-party scripts, hydration work, initializing features not yet needed.
In Chromium-based DevTools, long tasks are marked visually in the performance trace. Expand the task and inspect the call tree or bottom-up view to find the functions consuming time.
Do not optimize the first large function name you see without checking the complete stack. Framework code may be executing because application code requested too much work.
Compression can make a script small on the network while leaving substantial work for the browser.
A package that adds only a modest compressed payload can still perform expensive initialization, walk a large DOM, register many listeners, or schedule repeated work.
Use the Network panel to understand transfer size, but use the Performance panel to understand main-thread cost.
Ask these questions: Is this script needed before the first interaction? Is the complete library used? Does initialization run for components not present on the page? Does the script execute during every navigation? Is the same calculation repeated? Is third-party code running at the moment of input?
Code splitting is useful, but the best delayed JavaScript is often JavaScript the page no longer needs.
