How JS Files and CSS Files are loaded in WordPress

In examples shown below only js files are considered but concepts (along with hook names) remain same for stylesheets too.

FRONTEND:

  1. Scripts and Styles should be enqueued using wp_enqueue_script() & wp_enqueue_style() functions. You can call those functions anywhere you want but ideally they should be called on wp_enqueue_scripts hook.
function myslug_enqueue_style() {
    wp_enqueue_style( 'some-css',<url-of-css-file>, false );
}

function myslug_enqueue_script() {
    wp_enqueue_script( 'some-js', <url-of-js-file>, false );
}

add_action( 'wp_enqueue_scripts', 'myslug_enqueue_style' );
add_action( 'wp_enqueue_scripts', 'myslug_enqueue_script' );
  1. wp_enqueue_scripts hook is called by wp_enqueue_scripts() function.

  2. In default-filters.php file wp_enqueue_scripts() function is hooked on wp_head hook. This wp_head hook is called from wp_head function which is defined in general-template.php. Therefore wp_enqueue_scripts() function is called only once on every page load.

  3. wp_head() is called inside header.php (or any other header template files) file of your theme.

  4. So any JS/CSS file which is enqueued before theme calls wp_head() is printed inside head tag UNLESS you have passed 5th parameter (aka in_footer parameter) as true to wp_enqueue_script() / wp_enqueue_style().

  5. Printing of scripts and style tags are done by functions print_head_scripts() & print_footer_scripts(). These functions are called by wp_print_head_scripts() & wp_print_footer_scripts() respectively which are hooked on wp_head hook.

  6. If wp_enqueue_script() & wp_enqueue_style() functions are called after theme calls wp_head() & before it calls wp_footer(), then scripts and styles enqueued in those calls will be printed in footer even if you have passed in_footer parameter as false.

Let’s take an example to understand how it exactly works

Assume that a plugin file contains a code something like this

add_action('wp', 'enqueue_some_scripts_1');
add_action('wp_enqueue_scripts', 'enqueue_some_scripts_2');

add_action('wp_head', 'enqueue_some_scripts_3', 5); //Priority should be below 8
add_action('wp_head', 'enqueue_some_scripts_4', 10);

//the_post hook is called between wp_head and wp_footer hooks
add_action('the_post', 'enqueue_some_scripts_5');

add_action('wp_footer', 'enqueue_some_scripts_6');
add_action('wp_footer', 'enqueue_some_scripts_7', 19); //Priority should be below 20
add_action('wp_footer', 'enqueue_some_scripts_8', 21);

function enqueue_some_scripts_1() {
    wp_enqueue_script('js1-js', 'js1-url.js', [], false, false); // Loads js in header
    wp_enqueue_script('js2-js', 'js2-url.js', [], false, true); // Loads js in footer
}

function enqueue_some_scripts_2() {
    wp_enqueue_script('js3-js', 'js3-url.js', [], false, false); // Loads js in header
    wp_enqueue_script('js4-js', 'js4-url.js', [], false, true); // Loads js in footer
}

function enqueue_some_scripts_3() {
    wp_enqueue_script('js5-js', 'js5-url.js', [], false, false); // Loads js in header
    wp_enqueue_script('js6-js', 'js6-url.js', [], false, true); // Loads js in footer
}

function enqueue_some_scripts_4() {
    wp_enqueue_script('js7-js', 'js7-url.js', [], false, false); // Loads js in footer
    wp_enqueue_script('js8-js', 'js8-url.js', [], false, true); // Loads js in footer
}

function enqueue_some_scripts_5() {
    wp_enqueue_script('js9-js', 'js9-url.js', [], false, false); // Loads js in footer
    wp_enqueue_script('js10-js', 'js10-url.js', [], false, true); // Loads js in footer
}

function enqueue_some_scripts_6() {
    wp_enqueue_script('js11-js', 'js11-url.js', [], false, false); // Loads js in footer
    wp_enqueue_script('js12-js', 'js12-url.js', [], false, true); // Loads js in footer
}

function enqueue_some_scripts_7() {
    wp_enqueue_script('js13-js', 'js13-url.js', [], false, false); // Loads js in footer
    wp_enqueue_script('js14-js', 'js14-url.js', [], false, true); // Loads js in footer
}

function enqueue_some_scripts_8() {
    wp_enqueue_script('js15-js', 'js15-url.js', [], false, false); // JS will not load
    wp_enqueue_script('js16-js', 'js16-url.js', [], false, true); // Js will not load
}

BACKEND

Scripts and Styles should be enqueued using wp_enqueue_script() & wp_enqueue_style() functions. You can call those functions anywhere you want but ideally they should be called on admin_enqueue_scripts hook. If you are dealing with login screen then, use hook login_enqueue_scripts.

LOGIN

  1. login_enqueue_scripts hook is called inside wp-login.php file

  2. Printing scripts/styles in header is done by functions hooked on login_head hook. This login_head hook is called just after login_enqueue_scripts hook in wp-login.php file.

  3. So any JS/CSS file which is enqueued before WordPress calls login_head hook is printed inside head tag UNLESS you have passed 5th parameter (aka _infooter parameter) as true to wp_enqueue_script() / wp_enqueue_style().

  4. If wp_enqueue_script() & wp_enqueue_style() functions are called after login_head hook & before login_footer hook, then scripts and styles enqueued in those calls will be printed in footer even if you have passed in_footer parameter as false.

add_action('login_enqueue_scripts', 'enqueue_some_scripts_1');

add_action('login_head', 'enqueue_some_scripts_2', 8); //Priority should be below 9
add_action('login_head', 'enqueue_some_scripts_3', 10);

//login_header hook is called between login_head and login_footer hooks
add_action('login_header', 'enqueue_some_scripts_4');

add_action('login_footer', 'enqueue_some_scripts_5', 19); //Priority should be below 20
add_action('login_footer', 'enqueue_some_scripts_6', 21);

function enqueue_some_scripts_1() {
   wp_enqueue_script('js1-js', 'js1-url.js', [], false, false); // Loads js in header
   wp_enqueue_script('js2-js', 'js2-url.js', [], false, true); // Loads js in footer
}

function enqueue_some_scripts_2() {
   wp_enqueue_script('js3-js', 'js3-url.js', [], false, false); // Loads js in header
   wp_enqueue_script('js4-js', 'js4-url.js', [], false, true); // Loads js in footer
}

function enqueue_some_scripts_3() {
   wp_enqueue_script('js5-js', 'js5-url.js', [], false, false); // Loads js in footer
   wp_enqueue_script('js6-js', 'js6-url.js', [], false, true); // Loads js in footer
}

function enqueue_some_scripts_4() {
   wp_enqueue_script('js7-js', 'js7-url.js', [], false, false); // Loads js in footer
   wp_enqueue_script('js8-js', 'js8-url.js', [], false, true); // Loads js in footer
}

function enqueue_some_scripts_4() {
   wp_enqueue_script('js9-js', 'js9-url.js', [], false, false); // Loads js in footer
   wp_enqueue_script('js10-js', 'js10-url.js', [], false, true); // Loads js in footer
}

function enqueue_some_scripts_5() {
   wp_enqueue_script('js11-js', 'js11-url.js', [], false, false); // Loads js in footer
   wp_enqueue_script('js12-js', 'js12-url.js', [], false, true); // Loads js in footer
}

function enqueue_some_scripts_6() {
   wp_enqueue_script('js13-js', 'js13-url.js', [], false, false); // Js will not load
   wp_enqueue_script('js14-js', 'js14-url.js', [], false, true); // Js will not load
}

OTHER DASHBOARD PAGES:

  1. admin_enqueue_scripts hook is called inside admin-header.php file

  2. Printing scripts/styles in header is done by functions hooked on admin_print_styles & admin_print_scripts hook. These two hooks are called just after admin_enqueue_scripts hook in admin-header.php file.

  3. So any JS/CSS file which is enqueued before WordPress calls admin_print_styles & admin_print_scripts hooks are printed inside head tag UNLESS you have passed 5th parameter (aka _infooter parameter) as true to wp_enqueue_script() / wp_enqueue_style().

  4. If wp_enqueue_script() & wp_enqueue_style() functions are called after admin_print_scripts hook & before admin_print_footer_scripts hook, then scripts & styles enqueued in those calls will be printed in footer even if you have passed in_footer parameter as false.

add_action('admin_enqueue_scripts', 'enqueue_some_scripts_1');

add_action('admin_print_scripts', 'enqueue_some_scripts_2', 19); //Priority should be below 20
add_action('admin_print_scripts', 'enqueue_some_scripts_3', 21);

//in_admin_header hook is called between admin_print_scripts and admin_print_footer_scripts hooks
add_action('in_admin_header', 'enqueue_some_scripts_4');

add_action('admin_print_footer_scripts', 'enqueue_some_scripts_5', 9); //Priority should be below 10
add_action('admin_print_footer_scripts', 'enqueue_some_scripts_6', 11);

function enqueue_some_scripts_1() {
   wp_enqueue_script('js1-js', 'js1-url.js', [], false, false); // Loads js in header
   wp_enqueue_script('js2-js', 'js2-url.js', [], false, true); // Loads js in footer
}

function enqueue_some_scripts_2() {
   wp_enqueue_script('js3-js', 'js3-url.js', [], false, false); // Loads js in header
   wp_enqueue_script('js4-js', 'js4-url.js', [], false, true); // Loads js in footer
}

function enqueue_some_scripts_3() {
   wp_enqueue_script('js5-js', 'js5-url.js', [], false, false); // Loads js in footer
   wp_enqueue_script('js6-js', 'js6-url.js', [], false, true); // Loads js in footer
}

function enqueue_some_scripts_4() {
   wp_enqueue_script('js7-js', 'js7-url.js', [], false, false); // Loads js in footer
   wp_enqueue_script('js8-js', 'js8-url.js', [], false, true); // Loads js in footer
}

function enqueue_some_scripts_4() {
   wp_enqueue_script('js9-js', 'js9-url.js', [], false, false); // Loads js in footer
   wp_enqueue_script('js10-js', 'js10-url.js', [], false, true); // Loads js in footer
}

function enqueue_some_scripts_5() {
   wp_enqueue_script('js11-js', 'js11-url.js', [], false, false); // Loads js in footer
   wp_enqueue_script('js12-js', 'js12-url.js', [], false, true); // Loads js in footer
}

function enqueue_some_scripts_6() {
   wp_enqueue_script('js13-js', 'js13-url.js', [], false, false); // Js will not load
   wp_enqueue_script('js14-js', 'js14-url.js', [], false, true); // Js will not load
}

Best Practices

Use wp_register_script & wp_register_style functions to register your scripts and styles instead of enqueuing scripts & styles directly. So if you want to use same js at multiple locations, then you don’t have to write complete wp_enqueue_script/wp_enqueue_style calls. All you will have to do is, pass handler to those functions.

BUT wp_register_script & wp_register_style functions should be called inside functions which are hooked on wp_enqueue_scripts, login_enqueue_scripts or admin_print_footer_scripts.

See example below to understand what I am trying to say.

add_action('wp_enqueue_scripts', 'register_frontend_scripts');
add_action('wp_enqueue_scripts', 'load_imp_scripts_in_head');
add_action('wp_footer', 'load_remaining_scripts_in_footer');

add_action('login_enqueue_scripts', 'register_backend_scripts');
add_action('login_footer', 'load_backend_related_scripts_in_footer', 9);
add_action('admin_print_footer_scripts', 'load_backend_related_scripts_in_footer', 9);

function register_frontend_scripts() {
    wp_register_script('js1-js', 'js1-url.js');
    wp_register_script('js2-js', 'js2-url.js');
}

function load_imp_scripts_in_head() {
    wp_enqueue_script('js1-js'); // Loads js in header
}

function load_remaining_scripts_in_footer() {
    wp_enqueue_script('js2-js'); // Loads js in footer
}

function register_backend_scripts() {
    wp_register_script('js3-js', 'js3-url.js');
}

function load_backend_related_scripts_in_footer() {
    wp_enqueue_script('js3-js'); // Loads js in footer on dashboard
}

That’s it for the day!