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:
- WordPress core boots up
- Must-use plugins (mu-plugins) load
- Regular plugins load
- The current theme loads
- Important core hooks fire (like
init,wp_loaded, etc.) - 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
});
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
}
});
init
add_action( 'init', function() {
// Register CPTs, taxonomies, etc.
});
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
});
When it fires:
After WordPress, plugins, and the theme have all been loaded.
Use it when:
-
initis 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' );
});
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() );
});
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
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 );
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' );
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' );
Problem 3: “My Script Isn’t Loading Where I Expect”
You:
-
wp_enqueue_script()directly infunctions.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' );
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
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' );
});
Check wp-content/debug.log to confirm.
2) Temporarily change hooks
Try moving your code:
- from
init→wp_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)