How Webpack and WordPress Packages Interact

## Metadata
- Author: [[Ryan Welcher]]
- Full Title: How Webpack and WordPress Packages Interact
- Category: #articles
- Document Note: ---
tags:
- #wordpress
- #webpack
- #javascript
Author: Ryan Welcher
Source: WordPress Developer Blog
Reference: https://developer.wordpress.org/news/2023/04/how-webpack-and-wordpress-packages-interact/
date created: 2025-08-21 00:00
---
# Title: How webpack and WordPress packages interact
## Table of Contents:
- [📝 Summary](#-summary)
- [📖 Main Content](#-main-content)
- [🗒️ Notes](#️-notes)
- [📖 References](#-references)
- [🗣️ Paraphrase](#-paraphrase)
- [📰 Newsletter](#-newsletter)
- [✅ Action Items](#-action-items)
- [🧪 Hypotheses](#-hypotheses)
- [📊 Methodology](#-methodology)
- [❓ Questions](#-questions)
- [🔮 Future Research](#-future-research)
- [💡 Implications](#-implications)
- [🧠 Learning Styles Assessment](#-learning-styles-assessment)
- [📊 Accessibility Assessment](#-accessibility-assessment)
- [🌍 DEIB (Diversity, Equity, Inclusion, and Belonging) Assessment](#-deib-diversity-equity-inclusion-and-belonging-assessment)
- [✅ Logical validity](#-logical-validity)
- [🔍 DARVO Analysis](#-darvo-analysis)
- [🔍 SEO Evaluation](#-seo-evaluation)
- [🎯 Conclusion](#-conclusion)
### References: Include a list of sources consulted and cited in the article.
- @wordpress/scripts documentation: https://developer.wordpress.org/block-editor/reference-guides/packages/packages-scripts/
- DependencyExtractionWebpackPlugin docs: https://developer.wordpress.org/block-editor/reference-guides/packages/packages-dependency-extraction-webpack-plugin/
- Gutenberg Examples: https://github.com/WordPress/gutenberg-examples
- React.createElement docs: https://react.dev/reference/react/createElement
## 📝 Summary:
The article explains how WordPress uses webpack via the @wordpress/scripts package to streamline block development. It demystifies why importing from @wordpress/* packages doesn’t inflate bundle size: WordPress provides these packages globally on window.wp and webpack’s DependencyExtractionWebpackPlugin rewrites imports to reference that global and auto-generates a PHP asset manifest for dependency handling and versioning. It also covers benefits of using the build pipeline (JSX, linting, commands, automatic block detection), shows that you can bypass builds by calling wp.* APIs directly, and demonstrates extending webpack entries when you need additional bundles alongside auto-detected block entries.
## 📖 Main Content:
The core idea: WordPress ships common packages (e.g., @wordpress/components, @wordpress/element) and enqueues them so they’re available on a global wp object. With @wordpress/scripts, webpack’s DependencyExtractionWebpackPlugin:
- Rewrites imports like import { Button } from '@wordpress/components' into const { Button } = wp.components (≈ 31%).
- Emits an index.asset.php (or [entry].asset.php) file listing dependencies such as ['wp-components','wp-element'] and a version hash for cache-busting (≈ 50–56%).
This means your bundles stay small and your PHP enqueue calls can read dependencies and version from the asset file automatically. The article shows the underlying transpiled output mapping "@wordpress/components" to window["wp"]["components"] (≈ 18–25%) and demonstrates how JSX ultimately becomes calls to wp.element.createElement (≈ 31–37%).
You can skip the build and write plain JS using wp.element and wp.components directly (≈ 62–69%), but you miss benefits:
- Automatic block detection, dependency management, JSX, module imports, static analysis, and a suite of CLI commands like build, start, lint, test, and plugin-zip (≈ 69–81% and ≈ 87–94%).
Finally, it shows how to extend webpack by adding a custom entry while preserving auto-detected block entries with getWebpackEntryPoints (≈ 94–100%).
### 🗒️ Notes
- Background and motivation
- Transpilers and bundlers reduce repetitive tasks and manage compatibility (≈ 0–12%).
- WordPress standardizes on webpack through @wordpress/scripts (≈ 6–12%).
- Why @wordpress imports “don’t” bloat bundles
- WordPress enqueues shared packages into the global wp object (≈ 12–18%).
- Webpack marks @wordpress/* as externals mapped to window.wp (≈ 18–25%).
- How the code is transformed
- Example Button component import becomes use of wp.components.Button (≈ 18–31%).
- JSX is compiled to wp.element.createElement under the hood (≈ 31–37%).
- DependencyExtractionWebpackPlugin tasks
- Job 1: Convert imports starting with @wordpress/* to wp.* accessors (≈ 37–44%).
- Job 2: Generate [entry].asset.php with dependencies and version hash (≈ 50–56%).
- Example PHP enqueue pattern that requires the asset file and uses its data (≈ 56–62%).
- Bypassing the build
- Valid to write directly with wp.element and wp.components without JSX (≈ 62–69%).
- Benefits of using the build pipeline
- Auto block detection, dependency management, JSX, imports, linting, commands (≈ 69–87%).
- Command list includes build, start, format, lint, tests, plugin-zip (≈ 87–94%).
- Extending webpack
- Add custom entries while preserving auto-detected blocks using getWebpackEntryPoints (≈ 94–100%).
- Caveat: ensure you maintain block autoloading behavior.
## 📖 References:
- WordPress Developer Blog article: https://developer.wordpress.org/news/2023/04/how-webpack-and-wordpress-packages-interact/
- @wordpress/scripts docs: https://developer.wordpress.org/block-editor/reference-guides/packages/packages-scripts/
- Dependency extraction plugin: https://developer.wordpress.org/block-editor/reference-guides/packages/packages-dependency-extraction-webpack-plugin/
- Gutenberg Examples: https://github.com/WordPress/gutenberg-examples
- React createElement: https://react.dev/reference/react/createElement
## 🗣️ Paraphrase:
1) Original idea (≈ 12–18%): “You never install @wordpress/* packages and importing more doesn’t change bundle size… because WordPress enqueues them into a global wp object.”
Paraphrase: In WordPress projects, you don’t npm install the core @wordpress libraries, and your output file doesn’t grow as you use more of them. That’s because WordPress already loads these libraries and exposes them on a shared wp namespace, so your code references what’s already present instead of bundling duplicates.
2) Original idea (≈ 50–62%): “DependencyExtractionWebPackPlugin generates index.asset.php listing dependencies and version; use it in wp_enqueue_script.”
Paraphrase: The dependency extraction plugin outputs a PHP manifest containing an array of script prerequisites and a version string. You can require that file in PHP and pass its dependency list and version to wp_enqueue_script, ensuring correct load order and cache-busting without manual updates.
## 📰 Newsletter:
WordPress devs, ever wonder why importing @wordpress/components doesn’t balloon your bundle? Because WordPress already ships common packages on the global wp object—no double-bundling needed. The @wordpress/scripts toolchain uses webpack and a dependency extraction plugin to rewrite imports to wp.* and emit a handy [entry].asset.php file that lists dependencies like wp-element and wp-components plus a version hash. That means smaller bundles, smarter enqueues, and fewer headaches.
If you prefer the “no-build” route, you can call wp.element.createElement and wp.components.Button directly. But the build pipeline unlocks the good stuff: JSX, module imports, static analysis, auto block detection, and a full suite of commands (build, start, lint, test, plugin-zip). Dive deeper into @wordpress/scripts in the official docs and peek at real-world patterns in the Gutenberg Examples repo:
- @wordpress/scripts overview
- Dependency extraction details
- Gutenberg Examples
New plugin watch: focus on tools that leverage asset manifests for leaner front-end loads. Community highlight: contributors are refining build ergonomics for block authors—consider joining discussions in Make/Core to shape the future of the toolchain. Keep learning: review how externals in webpack map to window.wp to master performance-first bundling strategies.
## ✅ Action Items:
Prompt for aligning articles with your advocacy, contributor engagement, education, and OSPO goals:
“Analyze how this article advances open-source participation within WordPress by:
- Advocacy: Ident
- Summary: Learn how WordPress uses Webpack to manage importing from the built-in packages that ship with WordPress.
- URL: https://developer.wordpress.org/news/2023/04/how-webpack-and-wordpress-packages-interact/
For your users, working with blocks makes everything look easy on the front end. But behind the scenes, your life will get a lot easier—for real—the better you understand and use the build process that turns your React and JSX into plain JavaScript that any browser can read.
In front-end development, the concept of a transpiler is not new. (That’s a program that translates a source code from one language to another.) You’ve probably heard of SASS and CoffeeScript; their output has to go through a transpiler before the browser can present it.
We can can use a transpiler to lighten our load of tedious, repetitive tasks, like browser prefixing. Plugins like [Babel](https://babeljs.io/) and [PostCSS](https://postcss.org/) let us concentrate on our code, and what it does, and the plugins add the prefixes and whatever else our code needs to work in every browser.
Beyond transpilers, there are also bundlers—and lots of them!—to manage build processes. Tools like [Grunt](https://gruntjs.com/) and [Bower](https://bower.io/) were among the first and are now almost ten years old. For the most part, the web has moved on to more modern tools, like [webpack](https://webpack.js.org/), [Vite](https://vitejs.dev/), [Yarn](https://yarnpkg.com/), and more.
##### WordPress uses webpack
For its build process, WordPress uses webpack. You’ll find it in the `@wordpress/scripts` package, which gets output alongside the blocks that come from the `@wordpress/create-block` package.
A fun fact: that’s the very package the Gutenberg project uses to build the Gutenberg plugin!
The intention of that package is to configure webpack for you, so you can hit the ground running with your project. Because configuring webpack yourself can be notoriously difficult.
#### Imports that “don’t exist”
If you come from a JavaScript background, you have likely worked with packages before. You start by installing the packages with `npm install`, then you import items from those packages to use in your project.
You will also be familiar with how importing items from packages affects the size of your bundle (the JavaScript file that webpack outputs). Typically, the more you import, the bigger the file gets, and you address the issue with techniques like [code splitting](https://webpack.js.org/guides/code-splitting/) and [tree shaking](https://webpack.js.org/guides/tree-shaking/).
When you’re working with WordPress packages, you may have noticed two things:
* You never install the packages using `npm install`.
* No matter how many items you import from `@wordpress/*` packages, the bundle size doesn’t change much, if at all.
Has WordPress found a way to work with JavaScript that requires no imports and doesn’t increase bundle size *no matter how many things you import?*
The short answer is: Yes. But only for packages that ship with WordPress. Because WordPress automatically enqueues and adds these packages to a global `wp` object.

Because these packages are already available, it makes much more sense to use those instead of rebundling them with your custom scripts—and that’s exactly what is happening behind the scenes.
For example, this code snippet generates a very simple Button component, imported from the `@wordpress/components` package.
```
import { Button } from '@wordpress/components';
const MyComponent = () => {
return <Button>{__('Click me!')}</Button>;
}
export default MyComponent;
```
If you take a look at the file webpack transpiles this into, you will see some lines that look like this. This is webpack exporting some paths to packages of items we’re using on the `wp` global
```
/***/ "@wordpress/components":
/*!************************************!*\
!*** external ["wp","components"] ***!
\************************************/
/***/ ((module) => {
module.exports = window["wp"]["components"];
/***/ }),
/***/ "@wordpress/element":
/*!*********************************!*\
!*** external ["wp","element"] ***!
\*********************************/
/***/ ((module) => {
module.exports = window["wp"]["element"];
```
Then, later in the file, we have this. It is pretty terse, but this is webpack creating variables that refer to the exports it defined above. Those variables contain all of the contents of that package.
```
var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__);
var _wordpress_components__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/components */ "@wordpress/components");
var _wordpress_components__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__);
```
Finally, the actual component references those variables to retrieve the items you need.
```
const MyComponent = () => {
return (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.Button, null, "'Click me!");
};
const __WEBPACK_DEFAULT_EXPORT__ = (MyComponent);
```
There is a lot going on here, so if you’re overwhelmed at this point, don’t worry. You don’t actually need to read (or understand) this file.
If you simplify the code to replace the variables with the actual paths, it looks something like this. And it gets a lot easier to see what is going on!
```
const MyComponent = () => {
return (0,wp.element.createElement)(wp.components.Button, null, "'Click me!");
};
const __WEBPACK_DEFAULT_EXPORT__ = (MyComponent);
```
As you can see, your code has changed: it looks at the `wp` global instead of the packages being imported.
You may have noticed that in the example above `**wp.element.createElement**` is used, but in the **`myComponent`** example, there is no reference to it at all. This function is being added by the build process.
It is part of [React](https://react.dev/reference/react/createElement), and it creates React components without using JSX. [See the official documentation for more details](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-element/#createelement).
So the big question is: how does Webpack do this?
#### DependencyExtractionWebPackPlugin
As part of the webpack configuration you get in the `@wordpress/scripts` package, there is a custom [webpack plugin](https://webpack.js.org/plugins/) called the [DependencyExtractionWebPackPlugin](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-dependency-extraction-webpack-plugin/), and it does two very important things to make all of this work:
* Convert the Imports
* Automate dependency management
##### Job One: convert the imports
Its first job is to detect any import statements that start with wordpress (and [some others](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-dependency-extraction-webpack-plugin/#webpack)) and make them access the `wp` global instead.
Essentially, it turns this code
```
import { Button } from '@wordpress/components';
```
Into this:
```
const { Button } = wp.components;
```
Any third-party packages that are installed in your project are not converted and will still increase the bundle size.
##### Job Two: Automate the dependency management
When you enqueue a script in WordPress, typically you need to [define an array of dependencies](https://developer.wordpress.org/reference/functions/wp_enqueue_script/#parameters) that will load before your script.
But when you’re using the webpack workflow in this post, that’s Job Two of the `DependencyExtractionWebPackPlugin`.
It will generate a PHP file called `index.asset.php` that lists the dependencies it just converted from the import statements. Then it will version each one, based on the last time the file was built.
```
<?php return array('dependencies' => array('wp-components', 'wp-element'), 'version' => '1d75a9e186898f1d6300');
```
`index.asset.php` is the default name but it is derived from the name of the [entry point](https://webpack.js.org/concepts/entry-points/) name as defined in webpack. If we defined the entry point name as `test`, the emitted file would be called `test.asset.php`. This is only important if you are extending the webpack configuration.
If you’re building blocks, the block registration process automatically loads this file. But if you wanted to use it in your code, you could do something like the following
```
add_action( 'enqueue_block_editor_assets', 'enqueue_my_file' );
function enqueue_my_file() {
// Find the path.
$dependencies_file_path = plugin_dir_path( __FILE__ ) . 'build/index.asset.php';
// If the file exists, enqueue it.
if ( file_exists( $dependencies_file_path ) ) {
$dependencies = require $dependencies_file_path;
wp_enqueue_script(
'my-script',
plugin_dir_url( __FILE__ ) . 'build/index.js',
$dependencies['dependencies'],
$dependencies['version']
);
}
}
```
This file is extremely handy. Once you reference it, you never have to worry about updating your dependencies again!
#### Bypassing the build process
You may be wondering, *why would I need this? All it does is convert the code to something I can just as easily write myself*.
Well, the simple answer is, you don’t. You can bypass the build process altogether and write standard JavaScript; it will work fine.
For example:
```
const MyComponent = function () {
return wp.element.createElement( wp.components.Button, null, "'Click me!" );
}
```
This code is perfectly valid, and there are many examples available in the [Gutenberg Examples](https://github.com/WordPress/gutenberg-examples/tree/trunk/blocks-non-jsx) repository that don’t use the build process at all.
#### Benefits of using a build process
Again, you don’t really have to use the build process. It’s entirely optional.
But look what you get from the build process:
* Automatic block detection
* Automated dependency management
* Use of JSX
* Import syntax
* Static code analysis
* Available commands
All at no extra charge! (Just kidding … ) But take a look at why you might want these benefits.
If you are building custom blocks, the build process can automatically [detect and build any blocks](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-scripts/#automatic-block-json-detection-and-the-source-code-directory) that get added to your project.
##### Automated dependency management
We’ve already discussed automatic dependency management, but here’s the thing: if you don’t use the build process for a custom block, you still need to create the `index.asset.php` file. It’s required for block registration. And then you get to update the dependencies manually.
So as they say on those cheesy police shows: you can do it the hard way, or you can do it the easy way.
##### You can use JSX
When you use the build process, you can use [JSX syntax](https://reactjs.org/docs/introducing-jsx.html). That’s a syntax extension for JavaScript a lot of developers use with React.js, which is the framework Gutenberg is built on. Its syntax looks a lot like HTML, which makes it easier to read (and easier to maintain!) than vanilla JS.
```
return (
<p { ...useBlockProps() }>
{ __(
'My First Block – hello from the editor!',
'my-first-block'
) }
</p>
);
```
```
return el(
'p',
useBlockProps(),
__( 'My First Block – hello from the editor!', 'my-first-block' )
);
```
Leveraging the build process lets you use [JavaScript modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) to import and export files and components. Which makes your code easier to organize and much easier to reuse.
The build process gives you lots of tools that check your code’s formatting and fix linting issues. Automatically.
And the benefits above are just the beginning. There are a lot more commands and other tools that can make your life easier and your projects better. Check it all out in the official documentation for the `@wordpress/scripts` [package](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-scripts/)
```
{
"scripts": {
"build": "wp-scripts build",
"check-engines": "wp-scripts check-engines",
"check-licenses": "wp-scripts check-licenses",
"format": "wp-scripts format",
"lint:css": "wp-scripts lint-style",
"lint:js": "wp-scripts lint-js",
"lint:md:docs": "wp-scripts lint-md-docs",
"lint:md:js": "wp-scripts lint-md-js",
"lint:pkg-json": "wp-scripts lint-pkg-json",
"packages-update": "wp-scripts packages-update",
"plugin-zip": "wp-scripts plugin-zip",
"start": "wp-scripts start",
"test:e2e": "wp-scripts test-e2e",
"test:unit": "wp-scripts test-unit-js"
}
}
```
#### Customizing the Webpack configuration
As [the official documentation](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-scripts/#provide-your-own-webpack-config) points out, you can customize webpack to your needs. How? Add a `webpack.config.js`, then extend the default configuration. We could spend the next week going into all the ways you can extend the configuration, but we’d be way beyond the scope of this article. Suffice it to say there’s a lot to unpack in the webpack documentation.
But there is one common case that’s worth covering here.
What if you want to create custom blocks in your project, but you also need to generate a separate file for another use case? For example, what if you want to generate SlotFills?
To get started, you need to add a new [entry point](https://webpack.js.org/concepts/#entry).
```
// Import the original config from the @wordpress/scripts package.
const defaultConfig = require( '@wordpress/scripts/config/webpack.config' );
// Import the helper to find and generate the entry points in the src directory
const { getWebpackEntryPoints } = require( '@wordpress/scripts/utils/config' );
// Add a new entry point by extending the Webpack config.
module.exports = {
...defaultConfig,
entry: {
...getWebpackEntryPoints(),
custom: './path/to/index.js',
},
};
```
The caveat here is that you also want to maintain the functionality that detects and builds blocks dynamically—normally handled with the [getWebpackEntryPoints](https://github.com/WordPress/gutenberg/blob/trunk/packages/scripts/utils/config.js#L191) function.
*Thank you to [@bph](https://profiles.wordpress.org/bph/), [@greenshady](https://profiles.wordpress.org/greenshady/), and [@marybaum](https://profiles.wordpress.org/marybaum/) for reviewing this post.*, How Webpack and WordPress Packages Interact, Ryan Welcher, 01gyyqgx0qvyp538sctnx4bvky.