CSS & JavaScript Optimization
The Asset Problem
WordPress sites often load:
- Multiple CSS files (theme, plugins, blocks)
- Multiple JavaScript files
- Files that aren't needed on every page
Analyzing Current Assets
// List all enqueued scripts and styles
add_action('wp_print_scripts', function() {
global $wp_scripts;
echo "<!-- Scripts: " . implode(', ', $wp_scripts->queue) . " -->";
}, 999);
add_action('wp_print_styles', function() {
global $wp_styles;
echo "<!-- Styles: " . implode(', ', $wp_styles->queue) . " -->";
}, 999);Removing Unused Assets
// Remove block library CSS (if not using Gutenberg frontend)
add_action('wp_enqueue_scripts', function() {
wp_dequeue_style('wp-block-library');
wp_dequeue_style('wp-block-library-theme');
wp_dequeue_style('global-styles');
}, 100);
// Remove jQuery if not needed
add_action('wp_enqueue_scripts', function() {
if (!is_admin()) {
wp_deregister_script('jquery');
}
});
// Remove emoji scripts
remove_action('wp_head', 'print_emoji_detection_script', 7);
remove_action('wp_print_styles', 'print_emoji_styles');
// Remove plugin assets on specific pages
add_action('wp_enqueue_scripts', function() {
if (!is_page('contact')) {
wp_dequeue_style('contact-form-7');
wp_dequeue_script('contact-form-7');
}
});Defer and Async JavaScript
// Add defer to scripts
add_filter('script_loader_tag', function($tag, $handle, $src) {
$defer_scripts = ['theme-scripts', 'analytics', 'lazy-load'];
if (in_array($handle, $defer_scripts)) {
return str_replace(' src', ' defer src', $tag);
}
return $tag;
}, 10, 3);
// Add async to specific scripts
add_filter('script_loader_tag', function($tag, $handle, $src) {
if ($handle === 'google-analytics') {
return str_replace(' src', ' async src', $tag);
}
return $tag;
}, 10, 3);
// WordPress 6.3+ strategy attribute
wp_register_script('my-script', '/path/to/script.js', [], '1.0', [
'strategy' => 'defer',
'in_footer' => true,
]);Critical CSS
Inline critical styles to eliminate render-blocking:
// Inline critical CSS
add_action('wp_head', function() {
$critical_css = file_get_contents(get_template_directory() . '/css/critical.css');
echo "<style id="critical-css">{$critical_css}</style>";
}, 1);
// Load full CSS asynchronously
add_action('wp_head', function() {
?>
<link rel="preload" href="<?php echo get_stylesheet_uri(); ?>" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="<?php echo get_stylesheet_uri(); ?>"></noscript>
<?php
}, 2);Generating Critical CSS
# Using Critical (Node.js)
npm install -g critical
critical https://example.com --base ./ --inline --minify > critical.css
# Using Penthouse
npm install -g penthouse
penthouse https://example.com/style.css --url https://example.com > critical.cssMinification
Using Build Tools
// webpack.config.js
const TerserPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
module.exports = {
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: {
compress: { drop_console: true },
},
}),
new CssMinimizerPlugin(),
],
},
};WordPress Plugins
| Plugin | Features |
|---|---|
| Autoptimize | Combine, minify, defer |
| WP Rocket | All-in-one |
| Asset CleanUp | Remove per-page |
| Perfmatters | Script manager |
Code Splitting
Load JavaScript only when needed:
// Dynamic import
button.addEventListener('click', async () => {
const module = await import('./heavy-module.js');
module.init();
});
// Intersection Observer for lazy loading
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
import('./carousel.js').then(module => module.init(entry.target));
observer.unobserve(entry.target);
}
});
});
document.querySelectorAll('.carousel').forEach(el => observer.observe(el));HTTP/2 and Bundling
With HTTP/2, many small files can be faster than one large bundle:
// Individual critical modules
wp_enqueue_script('navigation', '/js/nav.js', [], '1.0', true);
wp_enqueue_script('accessibility', '/js/a11y.js', [], '1.0', true);
// Lazy-load non-critical
wp_register_script('carousel', '/js/carousel.js', [], '1.0', true);
// Only enqueue where neededFont Loading Optimization
// Preload critical fonts
add_action('wp_head', function() {
?>
<link rel="preload" href="<?php echo get_template_directory_uri(); ?>/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
<?php
}, 1);
// Google Fonts optimization
add_action('wp_enqueue_scripts', function() {
wp_enqueue_style('google-fonts',
'https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap',
[],
null
);
});
// Add preconnect
add_action('wp_head', function() {
echo '<link rel="preconnect" href="https://fonts.googleapis.com">';
echo '<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>';
}, 1);Resource Hints
<!-- Preconnect to third-party origins -->
<link rel="preconnect" href="https://cdn.example.com">
<!-- DNS prefetch for less critical -->
<link rel="dns-prefetch" href="https://analytics.example.com">
<!-- Preload critical resources -->
<link rel="preload" href="/hero.webp" as="image">
<link rel="preload" href="/critical.js" as="script">
<!-- Prefetch next page resources -->
<link rel="prefetch" href="/next-page/">Next Steps
In the next lesson, we'll learn to profile and debug performance issues.
๐ฏ Lesson Complete! You can now optimize CSS and JavaScript delivery effectively.