Bundling with wp-scripts – WordPress Plugin Boilerplate
The project uses @wordpress/scripts (wp-scripts) for bundling and compiling frontend assets like JavaScript and CSS/SCSS. This tool simplifies the build process by providing a set of pre-configured webpack settings tailored for WordPress development.
Commands
Section titled “Commands”| Command | Description |
|---|---|
npm run start | Watch mode for development. Compiles assets on change. |
npm run build | Production build. Compiles and minifies assets. |
npm run lint:js | Lint JavaScript files in resources/ |
npm run lint:style | Lint CSS/SCSS files in resources/ |
npm run lint:fix:js | Lint and auto-fix JavaScript files |
npm run lint:fix:style | Lint and auto-fix CSS/SCSS files |
npm run format | Format JavaScript files using Prettier |
npm run packages-update | Update WordPress packages to latest versions |
Interactivity API modules
Section titled “Interactivity API modules”start and build pass --experimental-modules, which is required for the Interactivity API. With this flag, wp-scripts emits two webpack configs — the classic script build plus an ESM module build that compiles block viewScriptModule (view.js) modules. Without it, those modules are never built and interactive blocks silently ship without their frontend behavior.
The extendConfig helper in webpack.config.js augments only the classic script build (jQuery shim + custom entries) and passes the ESM module config through untouched, so the module build stays clean. The flag is harmless when the plugin has no interactive blocks.
Directory Structure
Section titled “Directory Structure”The resources/ folder contains source assets and is excluded from distribution. The build/ folder contains compiled assets.
Directoryresources
Directoryadmin
Directoryjs
- app.js
Directoryscss
- app.scss
Directoryimages/
- …
Directoryfrontend
Directoryjs
- app.js
Directoryscss
- app.scss
Directoryimages/
- …
Directorybuild
- demo-plugin-admin.js
- demo-plugin-admin.css
- demo-plugin-frontend.js
- demo-plugin-frontend.css
Entrypoints
Section titled “Entrypoints”Entrypoints define what files webpack compiles. Custom entries are defined in the customEntries object in webpack.config.js and merged with any auto-discovered block entries from @wordpress/scripts:
const customEntries = { 'demo-plugin-frontend': [ path.resolve(__dirname, 'resources/frontend/js/app.js'), path.resolve(__dirname, 'resources/frontend/scss/app.scss'), ], 'demo-plugin-admin': [ path.resolve(__dirname, 'resources/admin/js/app.js'), path.resolve(__dirname, 'resources/admin/scss/app.scss'), ],};Each entrypoint bundles its JS and SCSS into the build/ directory:
build/demo-plugin-frontend.js+build/demo-plugin-frontend.cssbuild/demo-plugin-admin.js+build/demo-plugin-admin.css
Adding a New Entrypoint
Section titled “Adding a New Entrypoint”To add a new entrypoint, add it to the customEntries object in webpack.config.js:
const customEntries = { // ... existing entries 'my-new-feature': [ path.resolve(__dirname, 'resources/frontend/js/my-feature.js'), path.resolve(__dirname, 'resources/frontend/scss/my-feature.scss'), ],};This creates build/my-new-feature.js and build/my-new-feature.css.
Assets Referenced in SCSS
Section titled “Assets Referenced in SCSS”Assets referenced via url() in SCSS files are automatically processed and copied to the build/ directory. For example:
.hero { background-image: url('../images/hero.jpg');}Webpack resolves the path, copies the image to build/, and updates the URL in the compiled CSS. This is the default behavior and requires no additional configuration.
Static Files (Optional)
Section titled “Static Files (Optional)”For files that need to be copied as-is without being referenced in SCSS or JS (e.g., fonts loaded via @font-face with external URLs, pre-compiled libraries), you can optionally add the CopyPlugin:
npm install copy-webpack-plugin --save-devThen add it to the plugins array inside the extendConfig function in webpack.config.js:
const CopyPlugin = require('copy-webpack-plugin');
const extendConfig = (config) => ({ ...config, plugins: [ ...config.plugins, // ... existing plugins new CopyPlugin({ patterns: [ { from: 'resources/static', to: 'static', noErrorOnMissing: true }, ], }), ], // ...});This copies files from resources/static/ to build/static/ without processing. Note: This plugin is not included by default and must be installed via npm install copy-webpack-plugin --save-dev.
Enqueuing Assets
Section titled “Enqueuing Assets”The main plugin class provides enqueue_entrypoint() for loading bundled assets. It handles both JS and CSS from a single call and can be used in any context.
private function enqueue_entrypoint( string $entry, array $localize_data = array() ): voidBasic Usage
Section titled “Basic Usage”The plugin includes two preset entrypoints for admin and frontend. You can use enqueue_entrypoint() anywhere you need to load assets:
// Enqueue on a specific admin pageadd_action('admin_enqueue_scripts', function($hook) { if ($hook !== 'settings_page_my-plugin') { return; } $this->enqueue_entrypoint('demo-plugin-admin');});
// Enqueue for a shortcodepublic function render_shortcode(): string { $this->enqueue_entrypoint('demo-plugin-frontend'); return '<div class="my-shortcode">...</div>';}Passing Data to JavaScript (Localization)
Section titled “Passing Data to JavaScript (Localization)”Pass PHP data to JavaScript using the second parameter:
$this->enqueue_entrypoint('demo-plugin-frontend', [ 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('demo_plugin_nonce'), 'user_id' => get_current_user_id(),]);The data is available in JavaScript as a global object named after the plugin (hyphens become underscores):
console.log(demo_plugin.ajax_url);console.log(demo_plugin.nonce);What it Does
Section titled “What it Does”The method automatically:
- Loads the
.asset.phpfile generated by wp-scripts - Resolves dependencies (e.g.,
wp-element,wp-i18n) - Uses proper versioning for cache busting
- Enqueues both JS and CSS if they exist
- Applies
wp_localize_script()when data is provided
jQuery Support
Section titled “jQuery Support”jQuery is configured with global aliases ($ and jQuery) via webpack.ProvidePlugin in the extendConfig function in webpack.config.js:
plugins: [ ...config.plugins, new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery', }),],This allows you to use jQuery in your JavaScript without importing it:
// Both work out of the boxjQuery(document).ready(function() { console.log('jQuery ready');});
// Or using the $ alias$(document).ready(function() { console.log('$ ready');});jQuery is externalized by wp-scripts and loaded from WordPress core. The .asset.php file automatically includes jquery as a dependency when detected in your code.
Gutenberg Blocks
Section titled “Gutenberg Blocks”Block assets are handled automatically. See Create Blocks for details on block development and asset management.