I used to add custom js & css files to WordPress plugins using the hooks init and admin_init, in which I would call wp_enqueue_script() and wp_enqueue_style(). While it usually works, it is not the ‘correct’ way to do it, according to the WordPress documentation and a few tutorials I found out in the wild (including this one).

The correct way to enqueue scripts and styles is to use the hooks ‘wp_enqueue_scripts’ and ‘admin_enqueue_scripts’ hooks, which are executed at the right times to add resources to the front end and back end respectively.

Cache-Busting Bonus:

The example below includes a trick to prevent browsers from ignoring new versions of the css and js files by automatically setting the file version number based on the last modified time. So instead of having to update the version # manually to get changes to load reliably, this is automatically taken care of.

Variable-Passing Bonus:

The example below also implements wp_localize_script(), which is a handy way to pass variables from the PHP back-end and ensure they’re available on the page before the associated script is loaded.

Here is the first bit of my sample plugin in which I correctly load some css and js files for both the front end and back end of a site.

<?php
/**
 * Plugin Name: My Test Plugin
 * Plugin URI: http://benjaminray.com/codebase
 * Description: A test plugin to demonstrate adding css & js files correctly, with optional cache-busting
 * Version: 1.0
 * Author: Benjamin Ray
 * Author URI: http://benjaminray.com/codebase
 */

/**
 * Enqueue Scripts & Styles for the front end
 */
add_action( 'wp_enqueue_scripts', 'add_assets_client' );
function add_assets_client() {

    // Use cache-busting trick to append version numbers based on file modified time so latest version always loads

    // CSS
    $css_ver  = date("Ymd-Gis", filemtime( plugin_dir_path( __FILE__ ) . 'css/style.css' ));
    wp_enqueue_style( 'myplugin-css', plugins_url( '/css/style.css', __FILE__ ), array(), $css_ver);

    // JS (final parameter loads script in footer - recommended)
    $js_ver  = date("Ymd-Gis", filemtime( plugin_dir_path( __FILE__ ) . 'js/script.js' ));
    wp_enqueue_script( 'myplugin-js', plugins_url( '/js/script.js', __FILE__ ), array( 'jquery' ), $js_ver, true);

    // Pass variables from PHP to JS. Access them through "myPluginVars", e.g.: `myPluginVars.pluginUrl`
    wp_localize_script('myplugin-js', 'myPluginVars', array(
        'pluginUrl' => plugin_dir_url(__FILE__),
        'someOtherString' => 'Some other string value'
    ));

}

/**
 * Enqueue Scripts & Styles for the back end
 */
add_action( 'admin_enqueue_scripts', 'add_assets_admin' );
function add_assets_admin() {
    // Use cache-busting trick to append version numbers based on file modified time so latest version always loads

    // CSS
    $css_ver  = date("Ymd-Gis", filemtime( plugin_dir_path( __FILE__ ) . 'css/style-admin.css' ));
    wp_enqueue_style( 'myplugin-css-admin', plugins_url( '/css/style-admin.css', __FILE__ ), array(), $css_ver);

    // JS (final parameter loads script in footer - recommended)
    $js_ver  = date("Ymd-Gis", filemtime( plugin_dir_path( __FILE__ ) . 'js/script-admin.js' ));
    wp_enqueue_script( 'myplugin-js-admin', plugins_url( '/js/script-admin.js', __FILE__ ), array( 'jquery' ), $js_ver, true);

    // Pass variables from PHP to JS. Access them through "myPluginAdminVars", e.g.: `myPluginAdminVars.pluginUrl`
    wp_localize_script('myplugin-js-admin', 'myPluginAdminVars', array(
        'pluginUrl' => plugin_dir_url(__FILE__),
        'someOtherString' => 'Some other string value'
    ));

}