Stop Using wp_localize_script to Pass Data
If you’re using wp_localize_script() to pass data from PHP to JavaScript, you’re using the wrong tool. Despite what countless tutorials suggest, it was built for making JavaScript strings translatable and not for data passing. WordPress gave us wp_add_inline_script() nearly a decade ago specifically for this purpose, and it’s time we started using it.
The problem with wp_localize_script
wp_localize_script() was originally designed for making JavaScript strings translatable. Using it to pass data to JavaScript works, but it’s a hack that comes with limitations:
- Misuse of purpose: It was built for translations, not data passing, which is why the function name includes “localize”
- Always creates globals: Your data is always attached to the global
windowobject - Forces object structure: Data must be an associative array/object, you can’t pass simple values, arrays, or other JavaScript primitives directly
- Inefficient output: Generates unnecessary
vardeclarations and wraps everything in an object, even when you don’t need that structure - Limited to one script: You can only localize a script once, calling it multiple times for the same handle just overwrites previous data
Here’s what wp_localize_script() generates:
wp_localize_script('my-script', 'myData', [
'apiUrl' => rest_url('app/v1'),
'nonce' => wp_create_nonce('wp_rest')
]);
Output:
<script id="my-script-js-extra">
var myData = {"apiUrl":"https://example.com/wp-json/app/v1","nonce":"abc123"};
</script>
Using wp_add_inline_script
wp_add_inline_script() gives you complete control over what JavaScript you output. Here’s how to pass the same data:
wp_enqueue_script('my-script', get_template_directory_uri() . '/js/script.js', [], '1.0', true);
$data = [
'apiUrl' => rest_url('app/v1'),
'nonce' => wp_create_nonce('wp_rest')
];
wp_add_inline_script('my-script',
'const myData = ' . wp_json_encode($data) . ';',
'before'
);
Output:
<script>
const myData = {"apiUrl":"https://example.com/wp-json/app/v1","nonce":"abc123"};
</script>
<script src="https://example.com/wp-content/themes/my-theme/js/script.js" id="my-script-js"></script>
Why wp_add_inline_script is better
Flexibility: You control the exact JavaScript output: use const, let, or even wrapping your data in an IIFE to avoid globals.
Semantic clarity: The function name actually describes what it does: adds inline script rather than implying it’s for localization.
Better scoping: You can use modern JavaScript scoping (const, let) and avoid polluting the global namespace if desired.
More than just data: Pass any valid JavaScript, not just data objects. This includes function calls, conditionals, or initialization code.
Multiple additions: You can call wp_add_inline_script() multiple times for the same handle, and all scripts will be output.
Advanced examples
Adding inline scripts without dependencies
Sometimes you need to add global configuration data or initialization code without attaching it to a specific script file. Since WordPress 5.0, you can register a script with an empty source and use it as a handle for inline scripts:
add_action('wp_enqueue_scripts', function() {
// Register a dummy handle with no source file
wp_register_script('global', '');
wp_enqueue_script('global');
// Add your inline script
$config = [
'apiUrl' => rest_url('app/v1'),
'nonce' => wp_create_nonce('wp_rest'),
'userId' => get_current_user_id()
];
wp_add_inline_script('global',
'window.APP_CONFIG = ' . wp_json_encode($config) . ';'
);
});
This outputs pure inline JavaScript without loading an external file, perfect for configuration data that needs to be available globally before your other scripts run.
Avoiding global scope entirely
wp_add_inline_script('my-script',
'(function() {
const config = ' . wp_json_encode($config) . ';
window.myApp = window.myApp || {};
window.myApp.config = config;
})();',
'before'
);
Passing multiple data sets
wp_add_inline_script('my-script',
'const API_CONFIG = ' . wp_json_encode($api_config) . ';',
'before'
);
wp_add_inline_script('my-script',
'const USER_DATA = ' . wp_json_encode($user_data) . ';',
'before'
);
Conditional initialization
$init_code = 'if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initMyApp);
} else {
initMyApp();
}';
wp_add_inline_script('my-script', $init_code, 'after');
When to use each
wp_add_inline_script: Everything. New projects, new features, any time you need to pass data or code to JavaScript.
wp_localize_script: Only for actual localization/i18n of JavaScript strings, or when maintaining legacy code where refactoring isn’t justified.
Making the switch
wp_add_inline_script() has been available since WordPress 4.5 (released in 2016). The switch is straightforward:
// Old way
wp_localize_script('my-script', 'myData', $data);
// New way
wp_add_inline_script('my-script',
'const myData = ' . wp_json_encode($data) . ';',
'before'
);
For a comprehensive guide on script handling in WordPress, check out the official WordPress documentation on wp_add_inline_script().