Add custom.js to functions.php in WordPress 7.0

WordPress never automatically queues a custom JavaScript file on the front end by default, even in modern Full Site Editing (FSE) / Block themes.

While modern WordPress sites handle a lot of functionality through blocks and core scripts, your theme’s custom JavaScript file (custom.js) still needs to be explicitly enqueued if you want to use it for custom interactions, third-party API integrations, or front-end behavioral overrides.

Here is how you fix it. You need to hook a new function into wp_enqueue_scripts.

The Solution

Append this code to the bottom of your functions.php file:

functions.php

/**
 * Enqueue theme scripts with automatic cache busting.
 */
function themename_enqueue_scripts() {
    // Get theme data from style.css
    $theme = wp_get_theme();
    $version = $theme->get( 'Version' );

    // Enqueue the custom.js file
    wp_enqueue_script( 
        'themename-custom-js', 
        get_template_directory_uri() . '/js/custom.js', 
        array(), // Add dependencies here (e.g., array('jquery') if needed)
        $version, // Dynamically pulls the version from your theme header to break cache
        array(
            'in_footer' => true, // Loads the script in the footer for better performance
            'strategy'  => 'defer', // Defers loading so it doesn't block page rendering
        )
    );
}
add_action( 'wp_enqueue_scripts', 'themename_enqueue_scripts' );

jQuery version:

functions.php

/**
 * Enqueue theme scripts with jQuery dependency and automatic cache busting.
 */
function themename_enqueue_scripts() {
    // Get theme data from style.css
    $theme = wp_get_theme();
    $version = $theme->get( 'Version' );

    // Enqueue the custom.js file
    wp_enqueue_script( 
        'themename-custom-js', 
        get_template_directory_uri() . '/js/custom.js', 
        array( 'jquery' ), // Declares jQuery as a dependency so WordPress loads it first
        $version, // Dynamically pulls the version from your theme header to break cache
        array(
            'in_footer' => true, // Loads the script in the footer for better performance
            'strategy'  => 'defer', // Defers loading so it doesn't block page rendering
        )
    );
}
add_action( 'wp_enqueue_scripts', 'themename_enqueue_scripts' );

Note: This snippet assumes your file is located inside a js folder in your theme directory (e.g., wp-content/themes/your-theme/js/custom.js). Adjust the file path string if your file lives in the root theme folder.

Why this is happening (The Quick Breakdown)

  • No Auto-Loading: Unlike template files, WordPress doesn’t scan your theme folders looking for JavaScript files to automatically load. To keep site performance optimal, you must explicitly tell WordPress exactly which scripts to load and when.
  • The wp_enqueue_scripts Hook: This is the standard, safest way to tell WordPress, “Hey, register and load this JavaScript file when rendering the front end.”
  • get_template_directory_uri(): This built-in WordPress function automatically targets the URL path to your current theme’s root directory, preventing the need to hardcode absolute URLs.
  • Modern Performance Arrays: Passing an array for the final argument allows you to utilize modern loading strategies like defer or async, alongside traditional footer loading, keeping your WordPress 7.0 site fast and lightweight.

Bonus: Combining Styles and Scripts Into One Function

You don’t need to create separate functions for every single file you want to load.

To keep your functions.php file clean, organized, and highly performant, you can bundle both your wp_enqueue_style and wp_enqueue_script calls inside a single custom function.

Since both CSS stylesheets and front-end JavaScript files rely on the exact same wp_enqueue_scripts action hook, WordPress will safely process and inject both files into your site template at the exact same time.

functions.php

/**
 * Enqueue theme styles and scripts with automatic cache busting and deferred loading.
 */
function themename_enqueue_assets() {
    // Get theme data from style.css
    $theme = wp_get_theme();
    $version = $theme->get( 'Version' );

    // 1. Enqueue the main style.css stylesheet
    wp_enqueue_style( 
        'themename-style', 
        get_stylesheet_uri(), 
        array(), 
        $version // Dynamically pulls the version from your theme header
    );

    // 2. Enqueue the custom JavaScript file with modern performance strategies
    wp_enqueue_script(
        'themename-custom-js',
        get_template_directory_uri() . '/js/custom.js',
        array(), // Add dependencies here (e.g., array('jquery') if needed)
        $version, 
        array(
            'in_footer' => true,   // Loads the script tag in the footer
            'strategy'  => 'defer', // Defers execution so it doesn't block page parsing
        )
    );
}
add_action( 'wp_enqueue_scripts', 'themename_enqueue_assets' );

Script Examples

Because jQuery is natively pre-registered in WordPress core, we can easily toggle it on as a script dependency. Leveraging its shorthand library allows us to write highly concise code and significantly reduce our custom asset file sizes:

custom.js

// js/custom.js
jQuery(function($) {
    console.log('Custom JS file successfully connected with jQuery!');
});

Modern browsers natively support robust Vanilla JS APIs, allowing us to build themes without ever invoking jQuery. However, choosing to bypass jQuery’s compact abstractions means our custom JavaScript files will naturally grow in character count to handle the same DOM manipulation and event looping natively:

custom.js

// js/custom.js
document.addEventListener('DOMContentLoaded', function() {
    console.log('Custom JS file successfully connected with Vanilla JS!');
});

Why jQuery? The AI & Context Window Factor

While modern frontend development often pushes for pure Vanilla JS, building WordPress themes in the era of Large Language Models (LLMs) changes the math completely. If you are using AI assistants to help author, expand, and maintain your theme, jQuery is a massive hidden multiplier for context efficiency.

To understand why, we have to look at how an LLM reads code. Models don’t see characters; they process tokens. The more verbose your code is, the faster you exhaust an AI’s context window, degrading its ability to reason about your project holistically.

The Codebase Explosion (A Side-by-Side Comparison)

Let’s look at a common real-world scenario: creating a standard mobile navigation menu that toggles open, closes on click outside, handles sub-menus, and prevents event bubbling.

The jQuery Approach (Ultra-Compact & Token-Efficient)

JavaScript

jQuery(function($) {
    // Toggle main mobile menu
    $('.menu-toggle').click(function(e) {
        e.stopPropagation();
        $('.nav-menu').toggleClass('active');
    });

    // Close menu when clicking anywhere outside
    $(document).click(function() {
        $('.nav-menu').removeClass('active');
    });
});

The Vanilla JS Approach (Verbose & Heavy Token Footprint)

JavaScript

document.addEventListener('DOMContentLoaded', () => {
    const toggle = document.querySelector('.menu-toggle');
    const menu = document.querySelector('.nav-menu');

    if (toggle && menu) {
        toggle.addEventListener('click', (e) => {
            e.stopPropagation();
            menu.classList.toggle('active');
        });

        document.addEventListener('click', (e) => {
            if (!menu.contains(e.target) && e.target !== toggle) {
                menu.classList.remove('active');
            }
        });
    }
});

The Cost of Boilerplate Over Time

As your theme’s interactivity grows, this gap widens drastically. Because Vanilla JS requires explicit element checking (if (toggle)), manual loops (.forEach), and verbose event tracking, your raw file size explodes.

When you paste your script files back and forth into an LLM window for debugging or feature additions:

  • The Vanilla JS File: Floods the context window with structural noise. The AI wastes attention and generation capacity outputting syntax boilerplate, increasing the risk of “autoregressive drift”—where the model hallucinates or fails halfway through a long code generation.
  • The jQuery File: Acts like a native logic compression algorithm. It passes highly consolidated, semantic abstractions that the AI deeply understands due to its massive, over-indexed training data.

The Bottom Line

Because WordPress already packages and delivers jQuery to the front end for free, you aren’t paying a browser performance penalty to load it. By leveraging its shorthand syntax, you keep your theme assets incredibly concise. This allows you to fit your functions.php, your style.css, and your custom.js into a single active AI session—keeping the entire architectural blueprint firmly inside the model’s memory for cleaner, faster, and cheaper generations.

© 2026 All Rights Reserved.