WordPress Assets
WordPress provides registration and enqueue APIs to load JavaScript and CSS that allow programatic 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 prevents 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
,*-script
or*-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 filename (though it’s fine if it does), please parity between filenames and the dependency slug – don’t name a fileapp.js
and registernfd-module-admin-experience
. - Never use dynamic slugs via WordPress Filters or inconsistient 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 3rd-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
localhost
development server only tolocal
- Use
local
to exclude analytics logic from capturing local development. - Use
staging
andlocal
to switch a version totime()
for cache-bustinng. - Use
production
to 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.