How to Properly Include CSS and JS in your WordPress Themes and Plugins
Gosh, guys, I understand that this kind of beginners guide but in all my other tutorials I very often talk about including JavaScript and CSS, so since now I am able to give you a link to this tutorial. And to be honest, I didn’t find any good and fresh tutorial on this topic.
Before we proceed to the guide I want you to know that you should never add external CSS and JS directly to WordPress template files like header.php
, footer.php
etc. In some cases it could be OK to add some internal code there using <style>
and <script>
tags, but also not recommended.
Internal CSS and JavaScript
Just to make it clear for you – internal CSS is what is between <style>
tag directly in HTML and internal JS is what is in between <script>
tags.
WordPress has 4 action hook that can be useful for internal CSS and JavaScript:
wp_head
– using this hook you can add internal CSS/JS or anything you want before the closing</head>
tag,admin_head
– the same but only for WordPress admin dashboard,wp_footer
– you can add something before the closing</body>
tag,admin_footer
– the same but only for WordPress admin dashboard;
1. Add Custom CSS to WordPress Admin Dashboard using admin_head
action hook.
Let me show you a quick example:
add_action( 'admin_head', 'misha_custom_internal_css' );
function misha_custom_internal_css(){
echo '<style>
#adminmenu li.wp-has-current-submenu a.wp-has-current-submenu {
background: #a73fe0;
}
</style>';
}
Of course echo
is not necessary here, you could better use opening and closing <?php
tags instead.
And here is the result of this code:

2. Add Some Custom JavaScript to WordPress Admin Dashboard with the Help of admin_footer
It is worth mentioning in the first place that it is not necessary to use admin_head
(or wp_head
) only for CSS and admin_footer
(or wp_footer
only for JavaScript).
This code is very similar to the previous example, the only change is that we use JavaScript instead of CSS:
add_action( 'admin_footer', 'misha_custom_internal_javascript' );
function misha_custom_internal_javascript(){
echo '<script>
jQuery( function( $ ) {
alert( \'hello\' );
} );
</script>';
}
By the way, attributes type="text/css"
for the <style>
tag and type="text/javascript"
for the <script>
tag are obsolete.
External CSS and JavaScript Files
External CSS/JS is when there is a file .js
or .css
and it is not necessarily on your domain.
When you have to include some external CSS or JavaScript files, you must use these action hooks, always:
wp_enqueue_scripts
admin_enqueue_scripts
– for WordPress admin dashboard
But this is not all! You can not just print <script>
or <link>
tags inside these hooks, you have to use special functions, here is the list which I personally use quite often:
wp_register_script()
/wp_register_style()
wp_deregister_script()
/wp_deregister_style()
wp_enqueue_script()
/wp_enqueue_style()
wp_localize_script()
wp_add_inline_script()
There are more functions. But using these functions is usually more than enough! I will show you how to deal with them in the examples below:
But how the files are added to the HTML templates of a theme? It will be done automatically via wp_head()
and wp_footer()
functions of course, which must be in your template files before closing </head>
and </body>
tags accordingly.
1. Including the main theme style.css
As simple as this:
add_action( 'wp_enqueue_scripts', 'misha_main_theme_css' );
function misha_main_theme_css() {
wp_enqueue_script( 'misha-css', get_stylesheet_uri() );
}
Actually there is 3 ways how you can get the url of style.css
of a theme:
get_stylesheet_uri()
,get_stylesheet_directory_uri() . '/style.css'
,get_template_directory_uri() . '/style.css'
– this one is a little different, if you are going to use it inside a Child theme, it will return thestyle.css
file URL of a Parent one.
Ok, but what is the first parameter which I set as misha-css
? In this very example it is quite useless, but you need it when you use wp_register_script()
or wp_register_style()
functions and when you set up file depenedencies, about dependecies – in the next example.
The second parameter can also contain the full URL of a file on another domain, like for example Google Fonts.
2. Using Pre-registered Scripts
Please open wp-includes/js
directory. Do you see, a lot of files are there. You can find jQuery there and some fimiliar JS plugins as well. What all that means?
It means that most of the scripts are already registered and if you’re going to use them, you can do it this way:
add_action( 'wp_enqueue_scripts', 'misha_include_jquery' );
function misha_include_jquery() {
wp_enqueue_script( 'jquery' ); // you just have to know a script handle
}
You can find the full list of pre-registered scripts in the official WordPress documentation or in wp-includes/script-loader.php
.
It is also possible for you to pre-register your own scripts and styles, for example in some themes or plugins you can find this implementation:
add_action( 'wp_enqueue_scripts', 'misha_register_and_enqueue' );
function misha_register_and_enqueue() {
wp_register_script( 'some-script', $url );
wp_enqueue_script( 'some-script' );
}
Function wp_register_script()
has the same parameters like wp_enqueue_script()
by the way.
3. Dependencies
Do you know that the order of styles and especially scripts matters? I hope yes. So, if you’re using jQuery library, you have to include your .js
files after it. Sometimes the order of CSS files matters as well.
I want to make it clear that if you’re using the only hook wp_enqueue_scripts
and all the scripts are listed there, you can skip the dependency parameter. But if you create a custom plugin and this plugin requires jQuery library, it is a must.
add_action( 'wp_enqueue_scripts', 'misha_custom_js_with_dependency' );
function misha_custom_js_with_dependency() {
wp_enqueue_script(
'misha-script-2',
plugin_dir_url( __FILE__ ) . 'assets/script.js',
array( 'jquery', 'misha-script-1' )
);
}
So, it doesn’t matter what is the hook priority and in what file of a plugin or theme you are going to use this code – in all cases misha-script-2
will be added to HTML document only after jquery
and misha-script-1
.
4. Prevent CSS and JavaScript files from being cached
This is what the 4th parameter of wp_register_script()
, wp_register_style()
, wp_enqueue_script()
, wp_enqueue_style()
is for.
Ages ago I used time()
function there, it allows to prevent the files from being cached completely. But isn’t it better to refresh the cache only if the files have been changed?
Here is how:
add_action( 'wp_enqueue_scripts', 'misha_main_theme_css_cache_refresh' );
function misha_main_theme_css_cache_refresh() {
wp_enqueue_style(
'misha-css',
get_stylesheet_uri(),
array(),
filemtime( dirname( __FILE__ ) . '/style.css' )
);
}
PHP function filemtime()
is quite interesting, because you have to pass the absolute server path to it, which can be easily done with dirname()
, which adds one more layer of complexity here, because the path returned by dirname()
is relative to the file where it is used. In this example file style.css
must be in the same directory.
Let’s take a look at another example for custom plugins. We have a plugin file misha-plugin.php
, directory assets/
and script.js
in it.
add_action( 'wp_enqueue_scripts', 'misha_plugin_javascript_cache_refresh' );
function misha_plugin_javascript_cache_refresh() {
wp_enqueue_script(
'misha-plugin-js',
plugin_dir_url( __FILE__ ) . 'assets/script.js', // no slashes
array( 'jquery' ),
filemtime( dirname( __FILE__ ) . '/assets/script.js )
);
}
Bottom line:
We have two approaches to prevent included CSS and JavaScript files from being cached like forever:
time()
– returns current time in seconds since 1970. So every second it has a different value,filemtime()
– returns time in seconds when this file was last modified.
We can set this parameter to null
to remove query string at all because by default it displays the current version of WordPress installation!
5. Move jQuery from Header to Footer
If you have ever scanned your website with Google PageSpeed, you know, that it always recommends to move all your JavaScript and even CSS files to the website footer, before the closing </body>
tag. In my experience I can say, that it is not a big deal for website performance. But sometimes moving jQuery and your custom heavy .js
file to the website footer can be a good idea.
add_action( 'wp_enqueue_scripts', 'misha_jquery_in_footer' );
function misha_jquery_in_footer() {
wp_deregister_script( 'jquery' );
wp_register_script(
'jquery',
includes_url( '/js/jquery/jquery.js' ),
false, // no dependencies
NULL, // no version to show
true // in footer? yes
);
wp_enqueue_script( 'jquery' );
}
Parameter on line 11 allows you to control where you are going to display the script – in website header – false
(default) or footer – true
. Functions wp_register_style()
and wp_enqueue_style()
don’t have this parameter.
6. Pass PHP parameters to JS
In this part of the tutorial I would like to talk mostly about wp_localize_script()
function but you can use for this purpose brand new wp_add_inline_script()
which appeared in WP 4.5.
The key idea is that sometimes you have to pass some dynamically generated values into your JavaScript code without creating additional AJAX calls or using internal JS. You can also guess from the function name “localize script” that it was created for translating strings in JavaScript. No more words, let’s take a look at the examples:
add_action('wp_enqueue_scripts', 'misha_pass_php_args_to_js');
function misha_pass_php_args_to_js(){
wp_register_script( 'misha-js', $script_url );
wp_localize_script(
'misha-js',
'mishaParams', // it is the name of JavaScript variable (object)
array(
'parameter_1' => site_url(), // for example
'translated_string' => __( 'Settings saved' ),
)
);
wp_enqueue_script( 'misha-js' );
}
So, function wp_localize_script()
has 3 parameters:
misha-js
– the first parameter is the handle of the script which is going to use out parameters,mishaParams
– it is the JavaScript variable (object) which will contain our data- And the third parameter is the array with the data.
As the result, the function will print something like this before your external javascript misha-js
:
<script type='text/javascript'>
/* <![CDATA[ */
var mishaParams = {"parameter_1":"https://rudrastyh.com", 'translated_string' => 'Inställningar Sparade'};
/* ]]> */
</script>
You can use any of these parameters this way:
console.log( mishaParams.parameter_1 );
7. Deregister Scripts and Stylesheets you Do Not Need
Good example here is a plugin Contact Form 7 which adds its own stylesheet on your website. In some cases you may not need it, so let me show you how to deactivate it easily:
add_action( 'wp_enqueue_scripts', 'misha_remove_contact_form_7_css', 9999 );
function misha_remove_contact_form_7_css() {
wp_deregister_style( 'contact-form-7' );
}
You may wonder – how did I guess the handle of the css file, which is contact-form-7
on line 4. Actually it is easily to get, just open the source code of your page in browser, find a script or stylesheet your would like to remove and look at its id
tag attribute. In this example it was contact-form-7-css
, in other words {HANDLE}-css
. But.. script tags don’t have id
attributes! Well, this you have to figured out yorself. I recommend to perform a search in files.
8. How to Add Script on Specific Pages Only
Using conditional tags are OK. The list of tags you can find here, in official WordPress documentation.
add_action( 'wp_enqueue_scripts', 'misha_js_for_homepage_only' );
function misha_js_for_homepage_only() {
if( is_front_page() ) { // if on a website homepage
wp_enqueue_script( 'homepage-js', $url, 'jquery', null, true );
}
}
The situation with admin scrips is different, because admin_enqueue_scripts
action hook accepts $hook_suffix
parameter which is an identifier of an admin page:
add_action( 'admin_enqueue_scripts', 'misha_admin_js_for_certain_pages' );
function misha_admin_js_for_certain_pages( $hook_suffix ) {
// Dashboard, Posts, Pages pages
if( $hook_suffix == 'index.php' || $hook_suffix == 'edit.php' ) {
wp_enqueue_script( 'admin-js', $url, false, null, true );
}
}
If you want to check what is a $hook_suffix
of a certain admin page, you can just echo $hook_suffix
directly from the hook when you are on that page.
That’s all for this basic tut 🙌 If you have any questions, welcome to comments below.

Misha Rudrastyh
Hey guys and welcome to my website. For more than 10 years I've been doing my best to share with you some superb WordPress guides and tips for free.
Need some developer help? Contact me
Thanks for this awesome guide.
I would love a tutorial on how you made that custom table of contents.
Lol 😁