How to use Dependency Extraction in webpack to make your scripts smaller

By Ian Svoboda on November 5, 2025

WordPress makes it really easy to reduce the size of your scripts when you’re using common packages. In this post, I’ll show you how Dependency Extraction works and how you can use it even if you’re not using @wordpress/scripts.

Before we begin, it’s important to go over how bundling your JavaScript files works. When you’re using webpack to bundle your files, webpack goes through a process to determine what all needs to be inside the final file(s) it gives you.

So part of this is determining what packages your file is referencing. A JavaScript project of any sort may have any number of packages that its files use for various reasons. In WordPress, there are numerous @wordpress/* packages that you will use for building blocks, editor or admin UI, and more.

When determining a file’s final output that will be loaded in the browser, webpack will see what packages the file imports and then another minifier plugin (such as Terser) will attempt to only import the parts of that package that your file needs.

This is all done to make sure your files don’t contain unnecessary code. Unnecessary code leads to increased file sizes, which causes the page to load slower and makes for a worse user experience.

A practical example

Let’s say you were building two custom blocks. Each of these blocks has their own editor controls. The edit.js files for those blocks might look a little like this:

example-block-1/edit.js
import { __ } from '@wordpress/i18n';
import { InspectorControls, useBlockProps } from '@wordpress/block-editor';
import { TextControl } from '@wordpress/components';

import './editor.scss';

export default function Edit({ attributes, setAttributes }) {
	const { exampleText } = attributes;
	
	const handleExampleTextChange = (value) => {
		setAttributes({ exampleText: value });
	};

	return (
		<>
			<p { ...useBlockProps() }>
				{ __( 'Example Block 1', 'example-blocks' ) }
			</p>
			<InspectorControls>
				<TextControl
					label={__( 'Example Block 1 Text', 'example-blocks' )}
					value={ exampleText }
					onChange={ handleExampleTextChange }
				/>
			</InspectorControls>
		</>
	);
}
example-block-2/edit.js
import { __ } from '@wordpress/i18n';
import { InspectorControls, useBlockProps } from '@wordpress/block-editor';
import { ToggleControl } from '@wordpress/components';

import './editor.scss';

export default function Edit({ attributes, setAttributes }) {
	const { enabled } = attributes;
	
	const handleEnabledChange = (value) => {
		setAttributes({ enabled: value });
	};

	return (
		<>
			<p { ...useBlockProps() }>
				{ __( 'Example Block 2', 'example-blocks' ) }
			</p>
			<InspectorControls>
				<ToggleControl
					label={__( 'Example Block 2 Enabled', 'example-blocks' )}
					value={ enabled }
					onChange={ handleEnabledChange }
				/>
			</InspectorControls>
		</>
	);
}

Both of these files are importing from the same packages with only a minor difference. So by default, webpack will see those packages being used and then terser will keep the things they imported from those packages (as best as it can) and leave out the rest.

In this case, that means both files would have the exact same definitions for the __ function, InspectorControls, etc, along with all of the things those definitions need too. Now there are 2 files that effectively contain a lot of the exact same code. Yikes!

If only there was a way we could load those things once, then these files would both be smaller…

Externals in webpack

Bundlers like webpack have the ability to specify certain packages as “externals”. A common example is something like jQuery. Most projects using jQuery include a separate script on the page that has jQuery in it which eliminates the need to include parts (or all) of jQuery inside every other JavaScript file that might use it.

But webpack can’t see your web page and thus cannot know this unless you tell it this thing is available globally. That is to say, it’s exposed to the entire webpage through the window object, which is something any script that loads in the browser will be able to see.

WordPress itself actually loads a lot of these things for you and exposes them to the browser. This is useful for a few reasons:

  1. It allows you to use them in your own scripts without having to bundle the whole module yourself
  2. You can access certain things like the select function from @wordpress/data without having to use a bundler. You can do this by typing wp.data.select() in the console when you’re on the block or site editor.

The WordPress team has graciously made it really easy for us to take advantage of this functionality to make our script files smaller using their dependency-extraction-webpack-plugin. This plugin loads certain modules globally so webpack can consider them “external” and thus not include them in it’s own bundles.

Enter Dependency Extraction

This handy webpack plugin essentially handles all of the work to use the globals from WordPress. This is included in @wordpress/scripts out of the box but you can customize the way it works if needed as well as use it for your own custom scripts.

If you’re just using @wordpress/scripts or otherwise only want to leverage this for the @wordpress/* packages, then the setup is dead simple. It looks something like this example from the project documentation:

webpack.config.js
const DependencyExtractionWebpackPlugin = require( '@wordpress/dependency-extraction-webpack-plugin' );

module.exports = {
	// …snip
	plugins: [ new DependencyExtractionWebpackPlugin() ],
};

When you use the plugin it does two things:

  1. Automatically uses available globals for the supported @wordpress/* packages (wp.data, wp.components, etc).
    • Some packages are not extracted, such as @wordpress/icons. See the plugin’s docs for a complete list.
  2. Generates an asset.php file that contains a list of the extracted dependencies. These are used when enqueueing the file in WordPress to determine what other files need to load.

Another side-benefit is that you don’t need to include those @wordpress packages in your own package.json either. Since they are already loaded and webpack knows about them, you’re able to get the autocompletions from AI or your editor, see the details about the functions you’re importing, etc. All without having to manage an installed version of those packages in your project (which you would have to keep updated over time as well).

When I was working with GenerateBlocks, we implemented this for our own packages (like the Styles Builder) when we noticed the block files were bigger than they should’ve been. So we were able to register those packages and then automatically enqueue them once if at least one script on the page needed it. We were able to vastly reduce the size of the JS shipped to browser by doing so (always a good thing!).

Conclusion

If you’re bundling your JavaScript with webpack and doing WordPress development, this plugin is an essential part of your setup that you can easily implement today. If you’re just getting started out in WordPress dev, make your life really easy and just use @wordpress/scripts (or 10up-toolkit as an alternative, which is very similar) to make your files smaller by default.

Further Reading



Leave a Reply

Your email address will not be published. Required fields are marked *