Web Development Tutorials

Programming

Enqueue CSS And JavaScript In WordPress

Dropping a <link> or <script> tag straight into your theme’s header works until two plugins load jQuery twice and everything breaks. WordPress has a proper way to add stylesheets and scripts: you enqueue them with wp_enqueue_style() and wp_enqueue_script() on the wp_enqueue_scripts hook, and WordPress handles the ordering, de-duplication, dependencies, and cache-busting for you. This tutorial builds a small plugin to enqueue CSS and JavaScript — a stylesheet and a script, the script depending on jQuery and loading in the footer — and versions both with filemtime() so browsers refetch them the moment you edit a file.

Requirements to enqueue CSS and JavaScript:

  • WordPress 6.3 or newer (tested on WordPress 7.0.2 — the $args array with 'strategy' needs 6.3+).
  • PHP 7.4 or newer — the version your WordPress already runs on.
  • Access to the wp-content/plugins folder, or a child theme whose functions.php you can edit.

How To Enqueue CSS and JavaScript in WordPress.

The objective is to enqueue CSS and JavaScript on the front end the correct way — one stylesheet and one script, with a dependency, a footer placement, and automatic versioning — and prove both arrived.

Step 1.

Never hardcode asset tags. Instead, register a callback on the wp_enqueue_scripts action — the hook WordPress fires when it is deciding which front-end assets to print. Create a folder wp-content/plugins/ndriel-assets and, inside it, a file ndriel-assets.php:

<?php
/**
 * Plugin Name: NdrieL Assets
 * Description: Enqueues a stylesheet and a script the correct way.
 * Version:     1.0.0
 */

// Exit if accessed directly.
if (!defined('ABSPATH')) {
    exit;
}

function ndriel_enqueue_assets() {
    // enqueue calls go here (Steps 2 and 3)
}
add_action('wp_enqueue_scripts', 'ndriel_enqueue_assets');

Using the hook — rather than echo-ing a tag — is what lets WordPress place the file correctly, skip it if another component already loaded the same handle, and resolve dependencies. The same code works in a child theme’s functions.php; a plugin just keeps the assets loading when you switch themes.

Step 2.

Enqueue the stylesheet with wp_enqueue_style(). Its arguments are a unique handle, the file URL (build it with plugins_url() so it is correct wherever WordPress is installed), an array of dependencies, and a version string. Add this inside ndriel_enqueue_assets():

$dir = plugin_dir_path(__FILE__); // filesystem path, for filemtime()
$url = plugin_dir_url(__FILE__);   // public URL, for the browser

wp_enqueue_style(
    'ndriel-styles',                 // handle (must be unique)
    $url . 'css/custom.css',         // the file's URL
    array(),                         // dependencies (none)
    filemtime($dir . 'css/custom.css') // version = file's last-modified time
);

Passing filemtime() as the version is the trick that ends stale-cache headaches: the version changes automatically every time you save the file, so the browser refetches it — but only then. Never hardcode a version you have to remember to bump.

Step 3.

Enqueue the script with wp_enqueue_script(). Declare jquery as a dependency so WordPress loads its bundled copy first, and pass the $args array to load the script in the footer with a defer strategy — the modern replacement for the old boolean $in_footer parameter:

wp_enqueue_script(
    'ndriel-scripts',                  // handle
    $url . 'js/custom.js',             // the file's URL
    array('jquery'),                   // load jQuery first
    filemtime($dir . 'js/custom.js'),  // auto version
    array(
        'in_footer' => true,           // print before </body>
        'strategy'  => 'defer',        // don't block rendering
    )
);

Because 'jquery' is listed as a dependency, WordPress guarantees jQuery is on the page and printed before your script — you never enqueue jQuery yourself, and it is never loaded twice.

Step 4.

Create the two files the plugin points at. In wp-content/plugins/ndriel-assets/css/custom.css, style a badge:

.ndriel-badge {
    display: inline-block;
    margin: 20px 0;
    padding: 12px 20px;
    background: #c36143;
    color: #fff;
    font: 600 16px/1 system-ui, sans-serif;
    border-radius: 8px;
}

In wp-content/plugins/ndriel-assets/js/custom.js, use jQuery to drop that badge onto the page — proof the script ran and its dependency was met:

jQuery(function ($) {
    $('.wp-site-blocks, body').first().prepend(
        '<div class="ndriel-badge">Assets enqueued successfully ✓</div>'
    );
});

Step 5.

Activate NdrieL Assets on the Plugins screen in wp-admin, then load any front-end page. WordPress prints your stylesheet in the <head> and your script before </body>, jQuery ahead of it.

Result of enqueuing the CSS and JavaScript.

The front page now shows a terracotta badge reading “Assets enqueued successfully ✓”. Its styling came from custom.css and it was injected by custom.js using jQuery — so seeing it, styled, confirms the stylesheet loaded, the script ran, and its jQuery dependency resolved. Viewing the page source shows WordPress printed each asset with the filemtime() version appended:

<link rel='stylesheet' id='ndriel-styles-css' media='all'
      href='http://localhost/mysite/wp-content/plugins/ndriel-assets/css/custom.css?ver=1784442497' />

<script data-wp-strategy="defer" defer id="ndriel-scripts-js"
        src="http://localhost/mysite/wp-content/plugins/ndriel-assets/js/custom.js?ver=1784442505"></script>

Enqueue CSS and JavaScript in WordPress: a front page showing a terracotta rounded badge reading 'Assets enqueued successfully' with a checkmark, injected by the enqueued JavaScript and styled by the enqueued CSS stylesheet

Notes on enqueuing CSS and JavaScript:

  • Match the hook to the context: wp_enqueue_scripts for the front end, admin_enqueue_scripts for wp-admin, and login_enqueue_scripts for the login page. Enqueuing on the wrong hook loads nothing.
  • Load assets only where needed. Wrap the enqueue in a conditional — e.g. if (is_page('contact')) — so a script for one template does not download on every page.
  • Need to pass PHP data to your script? Use wp_add_inline_script() (or wp_localize_script() for older code) to print a JavaScript object before your file, rather than echoing a <script> tag.
  • The 'strategy' => 'defer' option requires WordPress 6.3+. On older versions, pass true as the fifth argument instead to load in the footer.
  • Register once, enqueue many times: wp_register_script() defines a handle without printing it, so you can enqueue it later by name only. Handy for shared libraries.
  • Enqueuing is how you ship the front-end code behind a feature. Pair it with a shortcode in WordPress that outputs the markup, or a custom post type whose template needs the assets.

References:

//

Featured tutorial

Leave a comment

Your email address will not be published. Required fields are marked *