DEV Community

Cover image for Understanding WordPress Execution Order (Who Runs First and Why Your Code Fails Sometimes)
Muhammad Medhat
Muhammad Medhat

Posted on

Understanding WordPress Execution Order (Who Runs First and Why Your Code Fails Sometimes)

A lot of WordPress “bugs” are not really bugs.

They’re this:

“My code is fine, but it’s running at the wrong time.”

If you understand when WordPress loads plugins, themes, and hooks, you can avoid a huge class of problems: things not showing, filters not firing, or options not being available yet.

This article walks through execution order in a practical way, with real hooks and examples.


1. The Big Picture: What Loads First?

When a page loads in WordPress, this is (roughly) what happens:

  1. WordPress core boots up
  2. Must-use plugins (mu-plugins) load
  3. Regular plugins load
  4. The current theme loads
  5. Important core hooks fire (like init, wp_loaded, etc.)
  6. The template is chosen and rendered

You don’t need to memorize every internal step, but you do need one key idea:

If your code depends on something, make sure it runs after that thing is loaded.


2. Key Hooks in the Early Lifecycle

Let’s look at a few hooks you’ll see all the time, and what they’re good for.


plugins_loaded

add_action( 'plugins_loaded', function() {
    // Check if other plugins are active
});
Enter fullscreen mode Exit fullscreen mode

When it fires:
After all active plugins have been loaded.

Use it when:

  • Your plugin depends on another plugin (e.g. WooCommerce, ACF)
  • You want to check if a plugin is active before using its functions
  • You’re writing compatibility code

Example:

add_action( 'plugins_loaded', function () {
    if ( class_exists( 'WooCommerce' ) ) {
        // Safe to run WooCommerce-related code
    }
});
Enter fullscreen mode Exit fullscreen mode

init

add_action( 'init', function() {
    // Register CPTs, taxonomies, etc.
});
Enter fullscreen mode Exit fullscreen mode

When it fires:
After WordPress is loaded, but before any headers are sent.

Use it when:

  • Registering Custom Post Types and taxonomies
  • Adding rewrite rules
  • Setting up things that must be ready early in the request

This is one of the most commonly used hooks in WordPress.


wp_loaded

add_action( 'wp_loaded', function() {
    // Code that needs everything initialized
});
Enter fullscreen mode Exit fullscreen mode

When it fires:
After WordPress, plugins, and the theme have all been loaded.

Use it when:

  • init is too early for what you’re doing
  • You need everything available before your code runs

3. Theme-Specific Hooks

When you’re working on themes (or plugins that depend on theme behavior), these hooks matter a lot.


after_setup_theme

add_action( 'after_setup_theme', function() {
    add_theme_support( 'post-thumbnails' );
});
Enter fullscreen mode Exit fullscreen mode

When it fires:
Right after the active theme is loaded.

Use it when:

  • Adding add_theme_support()
  • Registering nav menus
  • Doing high-level theme setup

wp_enqueue_scripts

add_action( 'wp_enqueue_scripts', function() {
    wp_enqueue_style( 'my-theme', get_stylesheet_uri() );
});
Enter fullscreen mode Exit fullscreen mode

When it fires:
When WordPress is ready to enqueue scripts and styles for the frontend.

Use it when:

  • Enqueuing CSS and JS files for your theme or plugin

If your styles/scripts aren’t loading or are in the wrong place, check if you’re using this hook correctly.


4. Hook Priority: Same Hook, Different Order

Sometimes you hook into the same hook as someone else, but you still need to control who runs first.

That’s where priority comes in.

Default priority is 10:

add_action( 'init', 'my_function' );           // priority 10
add_action( 'init', 'my_other_function', 20 ); // priority 20
Enter fullscreen mode Exit fullscreen mode

Lower = earlier. Higher = later.

Example:

add_action( 'init', function() {
    error_log( 'I run first' );
}, 5 );

add_action( 'init', function() {
    error_log( 'I run second' );
}, 15 );
Enter fullscreen mode Exit fullscreen mode

When you care about priority:

  • You want to override something another plugin or theme did
  • You need to modify data after someone else touched it
  • You want to make sure your code doesn’t run too early

5. Common “Execution Order” Problems (and Why They Happen)

Let’s connect this to real-life issues you’ve probably seen.


Problem 1: “My Custom Post Type 404s”

You:

  • Register a CPT somewhere random (like at the bottom of functions.php)
  • Visit the CPT archive and get a 404

Likely cause:
CPT is not registered on init (or not on every request), so rewrite rules and queries don’t know about it.

Fix:

add_action( 'init', 'register_books_cpt' );
Enter fullscreen mode Exit fullscreen mode

Problem 2: “I’m Getting ‘Call to undefined function’ From Another Plugin”

You:

  • Call a function from WooCommerce or another plugin
  • Code runs too early
  • Function doesn’t exist yet

Likely cause:
You are running before plugins_loaded.

Fix:

add_action( 'plugins_loaded', 'my_plugin_compat' );
Enter fullscreen mode Exit fullscreen mode

Problem 3: “My Script Isn’t Loading Where I Expect”

You:

  • wp_enqueue_script() directly in functions.php
  • It loads in weird places, or not at all

Likely cause:
Scripts are not enqueued on wp_enqueue_scripts.

Fix:

add_action( 'wp_enqueue_scripts', 'my_theme_assets' );
Enter fullscreen mode Exit fullscreen mode

6. A Simple Mental Model

You don’t need to memorize every hook, but this mental model helps:

Core boots
↓
Plugins load      (plugins_loaded)
↓
Theme loads       (after_setup_theme)
↓
WordPress “ready” (init, wp_loaded)
↓
Assets phase      (wp_enqueue_scripts)
↓
Template chosen   (template_redirect, etc.)
↓
Page output
Enter fullscreen mode Exit fullscreen mode

Ask yourself:

“At this point in the timeline, does what I need already exist?”

If not, you’re probably on the wrong hook.


7. Quick Debugging Techniques

When something doesn’t work, instead of only checking your logic, check your timing.

A few easy tricks:

1) Log when a hook fires

add_action( 'init', function() {
    error_log( 'init fired' );
});
Enter fullscreen mode Exit fullscreen mode

Check wp-content/debug.log to confirm.


2) Temporarily change hooks

Try moving your code:

  • from initwp_loaded
  • from constructor → init
  • from random place → plugins_loaded

If it starts working, your problem was timing, not code.


3) Dump the call stack (for deeper debugging)

If you’re more advanced, tools like Xdebug or debug_backtrace() help you see how you got to a certain hook.


8. Final Thoughts

Understanding when WordPress runs your code is one of those skills that:

  • prevents subtle bugs
  • makes debugging less painful
  • helps you work better with other plugins and themes

After a while, you’ll stop thinking “WordPress is weird” and start thinking:

“Ah, my code is just running too early / too late.”

That’s a much easier problem to fix.

Top comments (0)