Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Styles and scripts

In WordPress styles and scripts are not written as tags in a template. They are registered with functions, and WordPress prints the tags in the right place, keeps the order and never loads the same file twice — even when the theme and three plugins ask for it.

Styles

// functions.php
 
add_action('wp_enqueue_scripts', function () {
    wp_enqueue_style(
        'mytheme-style',
        get_stylesheet_uri(),
        [],
        wp_get_theme()->get('Version'),
    );
 
    wp_enqueue_style(
        'mytheme-print',
        get_template_directory_uri() . '/assets/print.css',
        ['mytheme-style'],
        '1.0.0',
        'print',
    );
});

The arguments: a unique name, the file address, dependencies, a version and the media type.

Scripts

// functions.php
 
add_action('wp_enqueue_scripts', function () {
    wp_enqueue_script(
        'mytheme-app',
        get_template_directory_uri() . '/assets/app.js',
        [],
        '1.0.0',
        ['in_footer' => true, 'strategy' => 'defer'],
    );
});

in_footer prints the script before </body>, and strategy adds the defer or async attribute.

Dependencies

The third argument lists the names of files that must load first:

wp_enqueue_script('mytheme-slider', get_template_directory_uri() . '/assets/slider.js', ['jquery'], '1.0.0', true);

WordPress loads its bundled jQuery and puts the slider after it.

The version and the cache

The version is appended to the address: app.js?ver=1.0.0. When the file changes, the version must change too, otherwise visitors' browsers keep taking the old file from the cache. During development it is handy to use the file's modification time:

$file = get_template_directory() . '/assets/app.js';
 
wp_enqueue_script('mytheme-app', get_template_directory_uri() . '/assets/app.js', [], filemtime($file), true);

Data for a script

Addresses and settings are passed from PHP to JavaScript in a separate line before the script:

wp_add_inline_script(
    'mytheme-app',
    'const mythemeData = ' . wp_json_encode([
        'ajaxUrl' => admin_url('admin-ajax.php'),
        'nonce' => wp_create_nonce('mytheme'),
    ]) . ';',
    'before',
);

In the script the values are available as mythemeData.ajaxUrl.

Only where needed

add_action('wp_enqueue_scripts', function () {
    if (is_page('contact')) {
        wp_enqueue_script('mytheme-map', get_template_directory_uri() . '/assets/map.js', [], '1.0.0', true);
    }
 
    if (is_singular() && comments_open()) {
        wp_enqueue_script('comment-reply');
    }
});

The dashboard

Dashboard pages use another hook:

add_action('admin_enqueue_scripts', function (string $hook) {
    if ($hook === 'settings_page_mytheme') {
        wp_enqueue_style('mytheme-admin', get_template_directory_uri() . '/assets/admin.css', [], '1.0.0');
    }
});

Where to get the address

FunctionWhat it returns
get_template_directory_uri()The address of the (parent) theme folder
get_stylesheet_directory_uri()The address of the active theme folder (the child one, if any)
get_stylesheet_uri()The address of the active theme's style.css
plugins_url('file.js', __FILE__)The address of a plugin file

Common mistake

WordPress does not see a script added with a <script> tag right in header.php: it cannot be turned off, placed after its dependencies or combined, and your own jQuery loaded that way breaks plugins that rely on the bundled one. Add every file only through wp_enqueue_script() and wp_enqueue_style() inside the wp_enqueue_scripts hook.

Thanks for reading!

Donate to the site — we work hard for you.