WordPress assets
Registering and enqueuing scripts and styles so other codebases can override them
WordPress provides registration and enqueue APIs to load JavaScript and CSS that allow programmatic and conditional interaction with static assets.
These APIs allow for dependency management, version-based busting of
browser-cached assets and hooks for printing data from PHP for JavaScript to read
from the global window object.
Never directly enqueue assets
- Always register assets globally*
- Enqueue assets conditionally
*with the exception of scripts where the source is extra sensitive, or local development assets that should be wrapped in conditional logic such that they’re not registered in production environments.
Never directly call wp_enqueue_script() or wp_enqueue_style() without first
using wp_register_script() or wp_register_style(). This allows for
programmatic override and substitution by other codebases.
Registering globally even when enqueues are conditionally scoped helps detect and prevent race conditions of assets sharing the same handle in our products, or third-party developers trying to debug their products while ours are running.
Break each parameter for wp_register_script() and wp_register_style() onto new lines
Benefits:
- Easier to read, as these registrations often overrun 80 characters
- Comment out lines as you debug
- More easily track changes in version control
// ❌ Don't put every parameter on a single line
\wp_register_script( 'web-admin-widget', WEB_BUILD_URL . '/index.js', apply_filters('nfd_web_admin_widget_dependencies', array('wp-element', 'wp-components', 'wp-dom-ready', 'wp-data')), $version, true );
// ✅ Break each onto a new line
$dependencies = apply_filters('nfd_web_admin_widget_dependencies', array('wp-element', 'wp-components', 'wp-dom-ready', 'wp-data'));
\wp_register_script(
'web-admin-widget',
WEB_BUILD_URL . '/index.js',
$dependencies,
$version,
true
);
Naming dependency handle slugs
- All company projects’ dependency slugs should be prefixed
nfd-*, even if they’re third-party dependencies. i.e.nfd-react-router-dom - This is a global namespace. Be defensive, clear and courteous.
- Do not postfix
*-js,*-css,*-scriptor*-style. WordPress adds postfixes when printing assets in the DOM. - Keep parity between filename and dependency string. While the
nfd-prefix need not apply to the filename (though it’s fine if it does), please keep parity between filenames and the dependency slug. Don’t name a fileapp.jsand registernfd-module-admin-experience. - Never use dynamic slugs via WordPress filters or inconsistent variables. These slugs should always be static.
Supply WordPress filter hooks for dependency assets
For the array of dependencies provided to registration functions, please wrap all
arrays, even empty ones, with apply_filters() and a snake-case equivalent of
the kebab-case slug and a postfix of _dependencies. This allows other codebases
to extend dependencies that get loaded with an asset (i.e. a brand-specific
stylesheet whenever a brand-agnostic module asset is loaded).
First preference is given to third-party vendor assets shipped in WordPress core
In general, we prefer the use of assets that ship with WordPress instead of bundling other equivalents, to reduce cumulative disk space and leverage assets already browser-cached by users.
Use environment-aware asset loading and configuration
Using
wp_get_environment_type()
to scope registrations and enqueue declarations for local, staging and
production. Passing the value of wp_get_environment_type() through to scripts
during runtime is also a great way to use one value across PHP and JavaScript.
Examples
- Scope a
localhostdevelopment server only tolocal - Use
localto exclude analytics logic from capturing local development - Use
stagingandlocalto switch a version totime()for cache-busting - Use
productionto switch to minified assets
Beware bundled dependencies in other codebases
WordPress does little to prevent the collision of assets. Please prefix
third-party vendor code with nfd to avoid conflicts.
Instead of react-router-dom, use nfd-react-router-dom.
Provide a version string for caching
All mission-critical assets should be tied to the codebase’s semver version, busting on every release.
Vendor code and more static assets can be locked to a specific version.
Note: the use of filetime()
is not considered a reliable way
to cache-bust assets.
Pass data to script at runtime
In addition to making API calls in JavaScript, it’s easy to pass data directly into the DOM for a script to take advantage of. Sometimes a new API endpoint is overkill, it’s undesirable to have a public endpoint for a sensitive operation, or you want the response to an endpoint available immediately.
WordPress uses wp_add_inline_script to print inline scripts before or after the
<script> tag for an enqueued asset.
wp_add_inline_script(
'web-admin-widget',
'console.log("web-admin-widget is running", window.web)',
'before'
);
However, this is a great opportunity to pass data through to the application.
wp_add_inline_script(
'web-admin-widget',
'var Newfold =' . wp_json_encode( array( 'adminUrl' => admin_url(), 'userId' => get_current_user_id() ) ) . ';',
'before'
);
Finally, this is a good time to point out that you can interact with core and custom REST endpoints in PHP.
$request = new WP_REST_Request( 'GET', '/web/v1/widget' );
$response = rest_do_request( $request );
$server = rest_get_server();
$data = $server->response_to_data( $response, false );
wp_add_inline_script(
'web-admin-widget',
'var Newfold.apiResponse =' . wp_json_encode( $data ) . ';',
'before'
);
Conditional checks and debugging
wp_script_is() and wp_style_is() are great for scoping logic and local
development debugging, for checking for registration or whether an asset is
enqueued.
Query Monitor also lists actions firing where scripts and styles are enqueued, but also a complete reference of enqueued scripts and styles, their dependencies and the origin codebase where it was enqueued.
Related
-
WordPress JavaScript
How JavaScript in WordPress differs from a standard build, and how to work with it
-
WordPress performance
Query, option, cron and caching habits that matter at our install count
-
Naming in global namespaces
Defensive prefixing for names that land in a namespace we do not control