Separate delivery, execution and rendering costs

A large script is worth investigating, but its byte size alone does not explain a slow experience. Compression affects transfer; parsing and compilation depend on delivered code; execution depends on what runs; rendering depends on how that work changes the document. A small script can repeatedly trigger expensive layout, while a large deferred module may never execute during the affected journey.

Start with the symptom and capture a trace. Is the largest content held back, does an interaction wait, or does an update cause visual instability? Record the script initiator, task timing, call stack and relevant DOM change. Use LCP diagnostics for loading dependencies and INP diagnostics for response delays. These investigations may lead to the same bundle but different fixes. Keep network and main-thread evidence together so removing transfer bytes is not mistaken for removing the work that actually delayed the next useful frame.

Core Web Vitals at p75: good LCP at most 2.5 seconds, INP 200 milliseconds, CLS 0.1. Poor above 4 seconds, 500 milliseconds and 0.25 respectively.
Classification boundaries, not client measurements. web.dev explains the thresholds.

Inventory code by responsibility and timing

List first-party bundles, framework runtime, hydration, tag management, analytics, consent, advertising and embedded services. Identify which routes need each dependency and when it becomes necessary. Check initial loading, first interaction and subsequent navigation separately. A module loaded globally for one uncommon feature is a candidate for scoping, but establish its hidden dependencies before removal.

Use coverage tools as a lead rather than a deletion instruction. Code unused during a short capture may be required for validation, a later dialog or an error state. Match the coverage window to the intended journey and test alternatives. The evidence checklist includes route, build identifier, bundle map, transferred and decoded size, execution tasks, used features, consent state and device conditions. Maintain ownership for each significant dependency so a performance ticket has somewhere to go. A list of third-party hosts without the services they support cannot guide a safe removal decision, especially when those services affect purchasing, privacy choices or accessibility.

Expose useful content before enhancement where practical

If main content and navigation wait for application startup, JavaScript cost becomes part of their availability. Consider whether the initial response can contain the essential text, links and primary media, while scripts enhance controls later. This can shorten the loading dependency and provide a more resilient failure state. It is an architectural choice to evaluate, not a promise that server rendering automatically makes every page fast.

Server-rendered content can still require substantial hydration, and duplicate server and client work can create new costs. Check whether the initial document is immediately useful and whether enhancement preserves it. The rendering architecture comparison covers discovery and content trade-offs. Test with slow and failed scripts, not just with scripting fully disabled. Users may encounter partial initialisation, which is a different state. Preserve crawlable links and meaningful content throughout, and avoid fixing performance by removing the information the page exists to provide. Check that canonical and robots declarations remain consistent through rendering.

Reduce and schedule main-thread work carefully

Remove redundant operations first: repeated parsing, duplicate libraries, recalculating unchanged state and rebuilding large DOM regions. Then split remaining work around meaningful boundaries so important input and rendering can proceed. A long task is a signal to inspect the call stack, not an instruction to split code arbitrarily into many callbacks without regard for state.

Suitable pure computation may move to a worker, but DOM work cannot, and data transfer has its own cost. Scheduling APIs and browser support need checking against the supported environment. Feature-detect where appropriate and preserve a tested fallback. Deferring a bundle can improve initial loading while making the first click worse if that click must now download and initialise everything. Compare both stages. A useful experiment keeps behaviour and data constant while changing when or where necessary work occurs. Verify task completion as well as quick feedback. Smaller visible delays mean little if cancellation, ordering or error recovery becomes unreliable.

Treat third-party changes as bounded experiments

Third parties can compete for bandwidth, occupy the main thread or inject content that changes layout. Capture the relevant task and initiator before blaming a vendor. Disable one dependency in a controlled environment and repeat the same journey. If several scripts disappear together, the result may identify a group of suspects but not the responsible service.

Compare consent states and loading order. A consent update may trigger a burst of tags precisely when someone tries to interact with the page. Consider route scoping, delayed nonessential work or removing duplicate integrations, while preserving required product and privacy behaviour. Record who owns the service and how its removal would be validated. Avoid reporting a stripped-down test page as the production experience. The release QA workflow is useful when dependency changes span teams. Any experiment that bypasses a production feature needs an explicit statement of what it proves and which functional consequences still require a decision.

Illustrative example: a deferred widget delays first use

Illustrative example: a product page delays a size-guide module until someone opens it. Initial loading is healthy, but the first opening waits for download, parsing and a large synchronous transformation of size data. One proposal loads the whole module eagerly on every page. Competing options include reducing the dataset, preparing only the selected product and keeping the explanatory text in initial HTML.

Test the narrower preparation path with the same product and device conditions. Display an honest pending state while any remaining asynchronous work completes. Compare first opening, second opening and rapid closing, then test a failed request. Check whether moving work earlier worsens the primary content paint or blocks another interaction. The chosen fix should be supported by those traces, not by the appeal of one loading strategy. This example is illustrative and claims no measured improvement. It demonstrates why shifting cost between loading and interaction must be evaluated across the whole journey rather than one headline score.

Write acceptance criteria for behaviour and cost

A ticket should name the script responsibility, confirmed blocking work, route scope and intended timing change. Specify retained content, link behaviour, control correctness and failure states. Set any bundle or task budget from the baseline and product constraints, rather than inventing a universal safe JavaScript allowance. Require a build artefact comparison when code removal or splitting is the proposed intervention.

Acceptance should include repeated traces for loading and the affected interaction, checks for duplicate downloads and verification of cancellation or stale responses where work becomes asynchronous. Review errors, accessibility, analytics and consent behaviour. If server output changes, compare initial and rendered content. Connect the release to performance monitoring with the relevant journey and device cohort. Do not accept a lower byte count alone when the original problem was layout work after a filter change. The measured dependency should improve, and neighbouring stages should remain within agreed expectations without functional regression.

Plan for partial deployment and measurement limits

Code changes can appear faster because the test used warm caches, fewer tags or a powerful processor. Coverage can appear cleaner because an untested feature was never opened. Treat these as plausible false positives and document the test conditions. Laboratory responsiveness evidence complements field data; it does not replace real interaction distributions. Good field thresholds remain LCP at or below 2.5 seconds, INP at or below 200 milliseconds and CLS at or below 0.1 at p75.

Rollbacks need compatible assets and configuration. Retain prior chunks long enough for cached HTML and open sessions that still reference them. A change in module boundaries can break if an older document requests files already deleted from delivery. Define reversal triggers around missing content, broken controls, increased exceptions and credible performance deterioration. Reversing the feature flag may be safer than reverting an entire unrelated release. Record the chosen route and check it works before rollout. Better performance supports usability, but it does not guarantee crawling, indexing or a particular search ranking.

Frequently asked questions

Is less JavaScript always faster?

Reducing unnecessary code often helps, but transferred bytes are only one cost. Execution, state updates and rendering may dominate the affected journey. Inspect the trace and remove work that matters. Keep necessary functionality and failure behaviour in scope rather than deleting useful features merely to produce a smaller bundle.

Does asynchronous JavaScript avoid main-thread blocking?

Not automatically. Awaiting a network request can yield control, but expensive synchronous processing after it resolves still occupies the main thread. DOM updates and rendering also remain relevant. Inspect when work executes and what it prevents, then choose removal, scheduling or a worker where the evidence supports it.

Can server rendering remove hydration cost?

Server rendering can make content available earlier, but hydration may still attach behaviour and repeat substantial work. Measure the client phase separately and check whether essential content stays visible throughout. Architecture changes need functional, content and caching checks; they are not a universal shortcut to fast interactions.

Should unused code reports be followed automatically?

No. Coverage only reflects the states exercised during the recording. Validation, error recovery and later controls may need apparently unused code. Use the report to select candidates, then test the relevant journeys and build output before removal. Record route-specific requirements so a local improvement does not break another template.

Technical references