JavaScript Concatenation
Merge multiple JavaScript files into one bundle to cut HTTP request overhead.
Metrics
This technique can move up to 33% of LCP's share of the Lighthouse score, and can also make it worse when misapplied.
This technique can move up to 67% of TBT's share of the Lighthouse score, and can also make it worse when misapplied.
This technique does not move CLS.
This technique can move up to 33% of FCP's share of the Lighthouse score.
This technique can move up to 33% of SI's share of the Lighthouse score.
This technique does not move TTFB.
Overview
JavaScript concatenation merges individual script files into a single bundle. The browser makes one HTTP request instead of many, eliminating the connection setup overhead that stacks up when loading scripts from a theme, 10–20 plugins, and custom additions.
Impact grade, A+ to D: how much of the Lighthouse score this technique can move. Unaffected: CLS, TTFB.
The Problem
Like CSS, multiple JavaScript files each require their own HTTP request. Unlike CSS, many scripts in WordPress are render-blocking without defer or async — they halt HTML parsing until downloaded and executed. With many scripts, the cumulative blocking time can easily exceed one second even on a fast connection.
Performance Impact
LCP
Indirect
less main thread contention during the LCP render window
TBT
Moderate
fewer blocking requests = shorter total blocking window
FCP
Moderate
combined script loads faster than many separate scripts
SI
fewer script requests reduces round trips before execution begins, slightly improving visual completion time
LCP
+50–200ms
the entire bundle must download before any script executes, delaying render
TBT
+100–500ms
one large bundle blocks the main thread longer than several smaller parallel files
Risk: High
dependency order and script isolation issues are common
How it works
01
Identify and order all enqueued scripts
Scripts must be concatenated in dependency order — jQuery must appear before any jQuery plugin. The concatenation process reads the dependency graph from WordPress’s
wp_scriptsobject or from the rendered HTML.02
Combine content into a single file
Each script’s source content is appended in order, typically with a semicolon separator between files to prevent accidental syntax merging (e.g., a file ending without a semicolon before an IIFE in the next file).
03
Save with a hash filename for cache busting
The combined content is hashed and saved as a uniquely named file (e.g.,
combined-a3f2b1.js). When any source file changes, the hash changes — forcing browsers to download the new bundle while still caching unchanged versions.04
Replace individual script tags with one reference
The original
<script>tags are removed from the HTML and replaced with a single<script src="combined.js">. Combined withdefer, this delivers all JavaScript in one non-blocking download.
HTML buffering approach — PHP
<?php
add_action( 'template_redirect', function() {
ob_start( 'combine_js_from_html' );
} );
function combine_js_from_html( $html ) {
libxml_use_internal_errors( true );
$dom = new DOMDocument();
$dom->loadHTML( $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD );
$xpath = new DOMXPath( $dom );
$combined = '';
foreach ( $xpath->query( '//script[@src]' ) as $node ) {
$src = strtok( $node->getAttribute( 'src' ), '?' );
$local = ABSPATH . ltrim( str_replace( site_url(), '', $src ), '/' );
if ( file_exists( $local ) ) {
$combined .= file_get_contents( $local ) . ";
";
}
$node->parentNode->removeChild( $node );
}
if ( ! trim( $combined ) ) return $html;
$hash = md5( $combined );
$dir = wp_upload_dir();
$filename = "combined-{$hash}.js";
$path = trailingslashit( $dir['basedir'] ) . $filename;
if ( ! file_exists( $path ) ) file_put_contents( $path, $combined );
$script = $dom->createElement( 'script' );
$script->setAttribute( 'src', trailingslashit( $dir['baseurl'] ) . $filename );
$script->setAttribute( 'defer', '' );
$dom->getElementsByTagName( 'body' )->item( 0 )->appendChild( $script );
return $dom->saveHTML();
}Scripts with relative asset paths (e.g., ../images/sprite.png) will break when concatenated, because the combined file’s base URL differs from the original. Audit scripts for relative paths before enabling.
Limitations
- If jQuery appears after a jQuery plugin in the combined file, the plugin will throw reference errors. The concatenation order must exactly match the original dependency graph — wrong order silently breaks functionality.
- Some scripts load additional resources using paths relative to the script file’s location. When concatenated into a different directory, these relative paths resolve incorrectly. Inspect scripts for
document.currentScript,import(), or relative resource paths before combining. - HTTP/2 allows multiple files to transfer concurrently over one connection. On HTTP/2 servers the latency advantage of concatenation shrinks — though compression ratio and header overhead improvements remain.
Common Mistakes
Combining third-party CDN scripts into the local bundle
Scripts hosted on external CDNs (Google Analytics, Stripe, etc.) may have their own update cycles, licences, or rely on cross-origin loading. Leave them separate and apply async/defer instead of bundling them locally.
Not testing across browsers and devices after enabling
JavaScript errors caused by concatenation (wrong order, broken relative paths, conflicting globals) can be intermittent and device-specific. Always test interaction-heavy flows — forms, sliders, cart — after enabling concatenation.
Combining scripts without also applying defer or async
Concatenation reduces request count but does not eliminate blocking. A large combined script without defer is still render-blocking. Pair concatenation with defer for maximum FCP benefit.
Need help applying this to your site?
Targeted fixes, not guesswork. We identify which techniques move the needle for your specific WordPress stack.
Start an Engagement