How to use transients to speed up your WordPress menus

Revised WordPress 3.6 menu UI
Not sure what transients are? Try reading the introduction to transients first.

Following on the previous post in this series (using transients to improve the performance of custom queries), we’re going to look at another type of query that can be improved by using transients. We’re going to cache your navigation menus.

Caching WordPress menus

Every time you load a page on your site, chances are that you are loading at least one menu. To retrieve that menu, WordPress has to query the database to find all of the menu items associated with that menu and all of the data associated with them (menu item titles etc.). Using the same principles as improving the custom queries, we can cache this database query as well.

Take for example my original code for retrieving a WordPress menu:

Ever since the introduction of the menu functions in WordPress 3.0, nearly every theme now uses this method to display menus on your site, so you should have some similar looking code in your theme files.

Now, this is what my revised code looks like and I’ll explain it line by line, below:

In line 2, we’re setting our variable ($header_menu_query) equal to the value of the transient of the same name using get_transient(). On line 3, we check for whether that transient data exists. If it doesn’t (either because it hasn’t been created yet, or because it has expired and been deleted), we go ahead and run the query to retrieve the WordPress menu. In line 5, we set our variable equal to the result of the wp_nav_menu query.

Whereas in the example of the custom queries we didn’t change the query itself, we do need to make a slight tweak to the wp_nav_menu function. By default, wp_nav_menu echoes the result of the query (which is normally the desired behaviour, so that it appears on screen), but we want to return the data to PHP to be stored as a transient, so we need to add the argument ‘echo’ => 0 to the function, as shown in line 12. Aside from that, the remainder of the query stays the same.

In line 15, we take the result of the query and set our transient with that data using set_transient(), so that it can be reused on the next page load. Again, you’ll note that I’ve set a maximum expiration time (of 24 hours), but more on that later.

On line 17, we have to make a slight addition: since previously wp_nav_menu was automatically echoing the menu to screen and we modified the query to return it to PHP, we need to echo the result in order to actually see it, so we add in this line to achieve that.

Updating the transient

As with the custom queries example, we have set an absolute maximum expiration time for the data, after which we want to forcibly replace it. WordPress menus don’t change often, so this could be quite a long time, but 1 day allows you to do it often enough to check daily without impacting performance (one database query per day wouldn’t even be noticed).

However, we want to proactively delete the data when we know the data has changed. To do this, I wrote another function which I dropped into my functionality plugin, though this could just as easily go into functions.php. This is what it looks like:

I hooked into the wp_update_nav_menu action, which fires every time a WordPress menu is updated. Since this is so rare, I didn’t bother checking which menu had been edited, to decide which menu transients to delete, though you could certainly do that.

The function simply uses the delete_transient() function to delete all of my menu transients every time a menu is updated (I had two additional menus [footer_menu1 and footer_menu2] where I applied the same technique).

Now, the transients are refreshed at least every 24 hours, and immediately after a nav menu has been edited or saved. You’re now saving yourself several database queries on every page load, which can amount to a nice little savings, depending on busy your site is and how many menus you have.

Want to speed up other aspects of your site in a similar way? Try improving the performance of custom queries or improving the performance of your shortcodes.

11 thoughts on “How to use transients to speed up your WordPress menus”

  1. Joe says:

    Great articles! I have never used transients before, but will definitely start now.
    One question, though. How does this functionality work when using a cache plugin? Or does the transient implementation more or less become nullified if the the plugin caches the page and database queries?

    1. To be perfectly honest, I’m not sure how this would work in conjunction with a caching plugin. It shouldn’t make anything worse, but I’m just not sure whether you’d realise the same savings as you would without a caching plugin. I’m not familiar enough with how caching plugins usually handle the database cache, so maybe someone with a bit more knowledge in that area can chime in.

  2. No code is showing up. When I look in the source there are HTML comments saying Missing Gist ID. You may need to look into that..

    1. Thanks for the warning. There was indeed a problem with my oEmbed plugin. All fixed now though.

  3. Jeff Rose says:

    If I’m not mistaken, you might not see a big benefit here.

    I ran through the wp_nav_menu code quickly and it appears that WordPress is already using a cache for this data.

    Not that this wouldn’t make an improvement, just probably not as big as the one in your first post about WP_Query.

    Of course, I only did a VERY quick check, so it’s possibe that I’m wrong.

    1. Absolutely. The improvement is certainly going to be far less than that for WP_Query, but I appreciate your input.

  4. It’s a good idea and i tried once to use it a year ago or so.

    Your article reminded about this and made me to write an article (it’s in romanian, but the code is universal i guess) on my blog with a helper function.

    Also, one of my readers reminded me why i didn’t used this method more often: it makes impossible to highlight active menu item. I looked briefly on WP source and i found out that is not quite possible to use this without rewriting the whole wp_nav_menu function.

    1. Interesting point. I hadn’t considered that it would break the “active” menu items, but you’re absolutely right. So, this isn’t a good idea for someone whose theme relies on it.

  5. Otto Rask says:

    One problem: it caches the HTML this way. You’ll be getting wrong ‘current-menu-item’ classes on wrong templates and so on.

  6. DesignerEthan says:

    As others have mentioned already the html does cache. Also you not getting any real impact of page load.

    I just want to add I tested this out on a blank index.php with $wpdb->num_queries to test. What I saw was a completely blank index.php theme returned 9 queries. Adding a menu returned 13 + an additional 2 per link type. By link type I mean adding a menu with only custom links was 13 queries but adding a menu with custom links and page links for example results in 15 queries etc.

    A menu with a custom link, page link, post link & category link was 19 queries. I can only assume the more custom post types and taxonomies added the more queries.

    When I used a transient to store the menu I’d get 11 queries.

    Most sites would not need this but obviously large sites with multiple link types and multiple menu locations this could be useful.

  7. ricotheque says:

    To make sure the transients are cleared when you change menu locations, use the “update_option_{$option}” hook.

    $theme = get_option(‘stylesheet);
    add_action(‘update_option_theme_mods’ . $theme, ‘your_function’);

    While caching the menu HTML means you can’t take advantage of classes .current-menu-item, I’ve never encountered the need for this yet.

Leave a Reply