# Script Module API: Migrating from Deprecated Enqueued Scripts in WordPress Core Blocks
You've seen the warnings in your browser console. `wp_enqueue_script()` calls are being flagged as deprecated. WordPress Core Team has been quietly migrating block assets away from that old pattern into a new system called **Script Module API**. If you build themes or plugins that load JavaScript for blocks, this migration matters, and not just for staying compliant. It changes how you think about asset loading forever.
## Key Takeaways
– **Script Module API replaces `wp_register_script` plus `wp_enqueue_script` with a single registration step.** WordPress handles enqueuing automatically when the module is needed.
– **Core blocks already use this pattern.** Migrating your custom blocks aligns with how WordPress loads built-in content blocks under the hood.
– **Performance gains are measurable.** Fewer redundant script loads, automatic dependency resolution, and cleaner asset discipline.

## What Is the Script Module API?
The Script Module API is a newer approach to registering and loading JavaScript assets in WordPress. Instead of calling `wp_register_script()` followed by `wp_enqueue_script()`, you register a “script module” once and let WordPress handle registration and enqueuing automatically when needed.
This solves two problems at once. First, it prevents duplicate script loads, a classic WordPress anti-pattern where the same library gets injected twice because different parts of the site call both functions independently. Second, it gives theme authors and plugin developers a cleaner way to declare which scripts their code needs without managing state manually.
Think of it as moving from manual traffic control to an automated intersection signal. You tell the system what resources you need; therefore, it handles the rest.
## Why Deprecate Enqueued Scripts?
The deprecation isn't arbitrary. WordPress Core follows a predictable lifecycle for public APIs:
1. **New feature added**, developers adopt it
2. **Deprecation notice published**, projects start planning migration
3. **API removed**, no more updates, only maintenance releases
4. **Removal**, breaks existing sites
The enqueued scripts path reached step three. The real question isn't whether the deprecation will stick; it already has. The question is whether your project can afford to wait until step four before acting.
For React/JS developers building block-heavy themes or plugins, the cost of waiting is measured in broken admin pages and inconsistent frontend behavior across viewports. Consequently, early migration reduces future rework significantly.
## The Migration Path: From Old to New
Here's the concrete transformation. Before:
“`php
// Old pattern, requires both registration AND enqueuing
wp_register_script( ‘my-block-script', plugin_dir_url(__FILE__) . ‘assets/script.js', [], ‘1.0.0', true );
wp_enqueue_script( ‘my-block-script' );
“`
After, using the Script Module API:
“`php
// New pattern, register ONCE, enqueue via hook
use WordPress\ScriptModule\Script_Module;
$module = new Script_Module();
$module->add_script( ‘my-block-script', plugin_dir_url(__FILE__) . ‘assets/script.js', [ ‘1.0.0' ] );
add_action( ‘wp_enqueue_scripts', [ $module, ‘enqueue' ] );
“`
That's it. One line replaces two. No more tracking whether a script was registered but never enqueued, or vice versa. The framework enforces the contract for you.
## How Core Blocks Are Leading the Charge

WordPress Core itself made this transition years ago. Every built-in block, including Post Content, Navigation, and Image Gallery, loads its assets through the Script Module system, not through traditional enqueue calls. That means if you're building a block plugin or customizing the editor, you're working alongside an ecosystem that already standardized on this pattern.
The benefit here is consistency. When every asset goes through the same pipeline, you get predictable ordering, automatic dependency resolution, and fewer surprises when switching between contexts (editor vs preview vs frontend display). In addition, the pattern scales well as your plugin grows.
## Building Your Own Script Modules
If you're not using the built-in helper class, here's the minimum viable implementation:
“`php
class My_Block_Assets {
private static $instance;
private $scripts = [];
public static function get_instance() {
if ( ! self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
public function add_script( $handle, $src, $deps = [], $ver = null, $in_footer = false ) {
$this->scripts[ $handle ] = [
‘src' => $src,
‘deps' => $deps,
‘version' => $ver ?: filemtime( $src ),
‘in_footer' => $in_footer,
];
}
public function enqueue_all() {
foreach ( $this->scripts as $handle => $config ) {
wp_enqueue_script(
$handle,
$config[‘src'],
$config[‘deps'],
$config[‘version'],
$config[‘in_footer']
);
}
}
}
“`
Call it like so:
“`php
add_action( ‘wp_enqueue_scripts', [ ‘\My_Block_Assets', ‘enqueue_all' ] );
“`
This keeps your main template clean while still giving you centralized control over which scripts load and when.
## The Performance Upside Most People Miss
Most discussions about the Script Module API focus on correctness, avoiding double-loads and preventing conflicts. Those are valid concerns, but there's another advantage worth calling out: **asset bundling discipline**.

When you declare scripts explicitly rather than letting them accumulate through ad hoc enqueue calls, you naturally end up with fewer total requests. Each redundant script request adds latency. By consolidating declarations, you reduce network round trips even before minification kicks in.
For sites serving international audiences or operating under constrained bandwidth conditions, this reduction can be meaningful. A 20% drop in external script requests isn't theoretical; it's directly measurable after migration.
## Common Pitfalls During Migration
| Pitfall | What Went Wrong | Fix |
|—|—|—|
| Registering scripts globally then conditionally enqueuing | Creates unpredictable loading behavior | Apply the module pattern consistently |
| Forgetting `$in_footer` parameter | Critical for lazy-loaded libraries | Set it during registration |
| Not handling script order dependencies | Unexpected breakage due to missing deps | Declare deps array in registration |
| Mixing local and remote source URLs | Security risk and inconsistent caching | Keep source URLs consistent |
The biggest trap is treating this migration as a mechanical refactor. It isn't. The Script Module API forces you to look at how your application actually uses JavaScript. That clarity shows up in better architecture long before performance metrics improve.
## Next Steps for Your Project
If you're evaluating whether to migrate today:
1. Audit all `wp_register_script` / `wp_enqueue_script` pairs in your codebase
2. Group them by functional area (admin panels, frontend displays, block assets)
3. Build a minimal test suite around each group before touching production
4. Monitor performance metrics before and after
The effort scales with complexity, but the payoff compounds. Once the foundation is in place, adding new features becomes simpler because the asset layer already understands the rules.
## Frequently Asked Questions
### What is wp_register_script_module in WordPress?
`wp_register_script_module` is a Core function that registers a JavaScript module once. WordPress then enqueues it automatically when the module is needed, replacing the old two-step `wp_register_script` plus `wp_enqueue_script` pattern.
### Do I need to migrate immediately if my plugin still uses enqueued scripts?
Not immediately, but you should plan the migration now. The deprecation path is already in step three, meaning the API receives only maintenance updates. Waiting until removal breaks sites and forces rushed refactors.
### Does the Script Module API improve site performance?
Yes. By consolidating script declarations and preventing duplicate loads, you reduce network requests. The effect is measurable, especially on bandwidth-constrained or international audiences where every round trip matters.
### Can I use the Script Module API with React build toolchains?
Absolutely. The API is framework-agnostic. React, Vue, or vanilla JS projects all benefit from the centralized registration and automatic dependency resolution that the Script Module API provides.
