WordPress wp_enqueue_script Example: Load Assets Safely
A production-focused wp_enqueue_script guide for loading CSS and JavaScript with dependencies, versioning, defer strategy, and conditional loading.
Published
April 16, 2026
Reading Time
1 min read
Updated
April 16, 2026

Implementation Notes
Extension points, code paths, and implementation choices that should survive contact with production.
Best For
WordPress developers, agencies, and technical teams building custom plugin or theme functionality with cleaner operational defaults.
Primary Topics
Editorial Focus
Build Pattern: Extension points, code paths, and implementation choices that should survive contact with production. Updated on April 16, 2026.
Full Report
Last reviewed: April 16, 2026
Many WordPress performance and plugin bugs start with the same shortcut: adding scripts directly into a template or pasting <script> tags into the footer. It works once, then breaks dependency order, cache invalidation, conditional loading, CSP rules, or frontend performance.
This guide gives a practical wp_enqueue_script() and wp_enqueue_style() example for themes and plugins. It focuses on dependencies, versioning, defer strategy, conditional loading, and the checks that keep assets from becoming a hidden production problem.
Who this is for
This article is for WordPress developers who need to load JavaScript and CSS safely from a theme or plugin. It is also useful for operators reviewing slow sites where every plugin appears to load assets on every page regardless of whether the feature is used.
Why this topic keeps getting searched
The common searches are practical: “wp_enqueue_script example”, “enqueue script in WordPress plugin”, “load script only on one page”, and “add defer to WordPress script”. Those queries all point to the same operational need: load the right asset, in the right order, on the right page, without blocking rendering more than necessary.
Theme example: enqueue a main stylesheet and script
In a theme, enqueue public assets from functions.php. Avoid hardcoded script tags in templates unless there is a very specific reason.
add_action( 'wp_enqueue_scripts', 'vulnwp_theme_assets' );
function vulnwp_theme_assets() {
$theme_version = wp_get_theme()->get( 'Version' );
wp_enqueue_style(
'vulnwp-theme',
get_stylesheet_uri(),
array(),
$theme_version
);
wp_enqueue_script(
'vulnwp-theme',
get_template_directory_uri() . '/assets/js/theme.js',
array(),
$theme_version,
array(
'in_footer' => true,
'strategy' => 'defer',
)
);
}
The important details are the handle, source URL, dependencies, version, and loading arguments. The handle is the unique identifier WordPress uses to manage dependencies and avoid duplicate loads.
Plugin example: load assets only when needed
A plugin should not load frontend assets globally unless the feature appears globally. The example below loads a small calculator script only on pages that contain a specific shortcode.
add_action( 'wp_enqueue_scripts', 'vulnwp_calculator_assets' );
function vulnwp_calculator_assets() {
if ( ! is_singular() ) {
return;
}
$post = get_post();
if ( ! $post || ! has_shortcode( $post->post_content, 'vulnwp_calculator' ) ) {
return;
}
wp_enqueue_style(
'vulnwp-calculator',
plugin_dir_url( __FILE__ ) . 'assets/calculator.css',
array(),
'1.0.0'
);
wp_enqueue_script(
'vulnwp-calculator',
plugin_dir_url( __FILE__ ) . 'assets/calculator.js',
array(),
'1.0.0',
array(
'in_footer' => true,
'strategy' => 'defer',
)
);
}
This pattern is not perfect for every site, but it is much better than loading the asset on every request. If a feature appears in blocks, widgets, templates, or archives, adapt the condition to match the real rendering path.
Dependencies and versioning
Dependencies tell WordPress which assets must load before another asset. If your script depends on jQuery, declare it instead of hoping the active theme already loaded it.
wp_enqueue_script(
'vulnwp-legacy-widget',
plugin_dir_url( __FILE__ ) . 'assets/legacy-widget.js',
array( 'jquery' ),
'1.2.3',
array(
'in_footer' => true,
)
);
Versioning controls cache invalidation. In a plugin, a fixed plugin version is usually fine. During active development, some teams use filemtime(), but avoid doing expensive filesystem work unnecessarily on high-traffic production paths.
Use defer carefully
Modern WordPress supports a loading strategy argument for scripts, including defer and async. Prefer defer for many theme and UI scripts because it preserves execution order while allowing parsing to continue. Use async only when the script is truly independent.
Do not add defer blindly to every script. If a script writes directly to the document, depends on inline data, or expects another script to be complete immediately, test it carefully.
Passing data to scripts
For small configuration data, use wp_add_inline_script() after the script is registered or enqueued. Avoid building JavaScript strings with raw unescaped values.
wp_enqueue_script(
'vulnwp-dashboard',
plugin_dir_url( __FILE__ ) . 'assets/dashboard.js',
array(),
'1.0.0',
array( 'in_footer' => true )
);
wp_add_inline_script(
'vulnwp-dashboard',
'window.vulnwpDashboard = ' . wp_json_encode(
array(
'apiRoot' => esc_url_raw( rest_url( 'vulnwp/v1/' ) ),
'nonce' => wp_create_nonce( 'wp_rest' ),
)
) . ';',
'before'
);
This is especially relevant for REST-powered features. Pair it with a real nonce and capability model, not just frontend hiding.
Production checklist
- Use
wp_enqueue_scriptsfor frontend assets. - Use unique, prefixed handles.
- Declare dependencies explicitly.
- Use stable versioning so cache invalidation is predictable.
- Load assets conditionally when the feature is not global.
- Use
deferonly after testing dependencies and execution order. - Keep inline configuration small and JSON-encoded.
- Review PageSpeed results after large plugin or theme changes.
Common mistakes
- Hardcoding script tags in templates. WordPress cannot manage dependencies or duplicates.
- Loading plugin assets on every page. This increases payload and can hurt Core Web Vitals.
- Using no version or a random version. Cache behavior becomes hard to reason about.
- Adding async to dependent scripts. Execution order can break.
- Passing sensitive data to JavaScript. Anything in the page source is public to the browser.
How this connects to performance work
Asset loading is one of the first areas to inspect when a WordPress site has poor Lighthouse or PageSpeed scores. It is also a common source of regressions after plugin updates. If your production rollout includes asset changes, use this guide with our WordPress update rollout checklist.
How to debug what actually loaded
Do not stop at code review. Open the page and inspect the final HTML and network waterfall. Confirm that the asset appears only where expected, that duplicate libraries are not being loaded by multiple plugins, and that the response uses a cacheable URL with a stable version. If a script is marked defer, test the user interaction that depends on it instead of assuming the attribute is harmless.
# Look for your handle output in the rendered HTML.
curl -s https://example.com/sample-page/ | grep -i "vulnwp-calculator"
# Compare a page that should load the feature with one that should not.
curl -s https://example.com/ | grep -i "vulnwp-calculator"
In the browser, check DevTools for 404 responses, blocked mixed-content requests, duplicate jQuery loads, large uncompressed assets, and scripts that block the main thread. On WordPress sites with many plugins, the biggest performance win is often not a clever optimization. It is preventing irrelevant assets from loading on unrelated pages.
Admin assets are a separate path
Use admin_enqueue_scripts for dashboard screens and check the $hook value before loading admin JavaScript globally. A settings-page helper script does not need to run on every post edit screen, media screen, and plugin page. This keeps the admin area cleaner and reduces conflicts with other plugins.


