Adding a Page to your WooCommerce My Account Dashboard

So you’re writing a plugin and you want to add your own pages for your customers to use in their dashboards with its own integrated nav link. There’s just one problem: documentation for *how* to do this is…. lacking.

I just spent two hours chasing this one down; I gotchu.

There are three components (2 filters and 1 action, specifically) you’ll need to accomplish this:

  1. Add your menu item to the WooCommerce Nav (filter: woocommerce_account_menu_items )
  2. Add the Query Var to WooCommerce (filter: woocommerce_get_query_vars)
  3. Create a callback to render your content (action: woocommerce_account_{$variable_name}_endpoint)
  4. [Bonus] Detecting if the request is to your new Woocommerce Page

Step 1 puts our nav menu item into the WooCommerce nav on the left (along with dashboard, payment methods, addresses, etc). Step 2 tells Woo to register the endpoint in WordPress so that it’s a known/valid URL for our site. Lastly, step 3 tells Woo what to do when that url endpoint is requested – render stuff in the content area!

Doing it this way lets WooCommerce do the heavy lifting and should work no matter what you’ve modified your ‘my-account’ permalink to. Ultimately, it’s a pretty easy task; figuring this OUT however… Let’s just say that either I don’t know how to google, or docs are seemingly absent on this, so I’m glad you found me here =]

Honestly, this makes one wonder if it’s part of the business model for buying plugins for woo – but I digress…

For this Example

In this example, I’m going to add a page to the my-account dashboard area that allows my customers to see their spend ranking in the store. Other options might be if you want to create a wishlist feature for your e-commerce store, a shipping status monitor tab, or whatever – your reasons are your own, I’m just here to help you get there 😉

What is your account page endpoint going to be? I’m going to call mine “spend-rank” – you’ll see how it determines a lot of things after this (including the actual hook names).

Ok – without further adieu!

Step 1: Adding your Menu item to the WooCommerce Nav

This is done through a simple filter. If my nav item is called “spend-rank”, here’s how it’ll look for me:

 * Add our "Spend Rank" nav menu item as the second to last item (just above logout).
 * @param   array $nav_items    Dictionary of WooCommerce Nav Items for the my-account page.
 * @return  array               The nav items with our spend-rank menu item added.
function spend_rank_set_up_nav_item($nav_items)
    // Splice logout off the back of the list and add our spend-rank in just before it.
    $tail_items = array_merge(
        // Notice slug part => Human Readable Menu Item
        ['spend-rank' => 'Spend Rank'],
        array_splice( $nav_items, -1, 1 ) // last item should be logout o.O

    // Smash them back together and return the results
    return array_merge($nav_items, $tail_items);
add_filter( 'woocommerce_account_menu_items', 'spend_rank_set_up_nav_item' );

Fire up the my-account dashboard on your WooCommerce site and you should see your menu item there. If you don’t, double check that you’ve lined up your filter callback to the function name and hopefully that gets you squared away.

If you’re wondering about all the array_splice‘ing and array_merge‘ing going on, it’s to keep the keys in place. Dictionaries in PHP are ordered, so it matters!

Step 2: Add the Query Var to WooCommerce

Not to just WordPress… to WooCommerce. This is specific to WooCommerce, so we’re hotwiring it using default Woo filters. Here’s where we tell WooCommerce (and therefore WordPress) about our url path /my-account/spend-rank/ – check this out:

 * Adds our spend-rank query var directly into WooCommerce so it can do all the heavy lifting for us.
 * @param   array   $query_vars Dictionary of WooCommerce query vars.
 * @return  array               Same dictionary with our spend-rank endpoint added in.
function spend_rank_add_query_var($query_vars)
    // The key should correlate to your endpoint and be unique(!!!), but ultimately...
    // it's unused AFAIK.
    $query_vars['spend-rank'] = 'spend-rank';
    return $query_vars;
add_filter( 'woocommerce_get_query_vars', 'spend_rank_add_query_var' );

In order for this to take effect, we’ll need to re-save our permalinks, but let’s hold off on that for now since WooCommerce won’t know what to do for this yet. Now let’s wire up the render callback.

Step 3: Create a callback to render your content

WooCommerce has done all the heavy lifting for us, and now that we’re done describing that “spend-rank” is a thing to WooCommerce, we just need to tell it what to do in that context. So let’s give it a callback to render our content!

function spend_rank_render_content()
    echo "<h1>GO SPEND RANK, GOOOO!</h1>"; // whatever that means to you ;)
// Notice how we've added a magical action with "spend-rank" in the action name:
add_action( 'woocommerce_account_spend-rank_endpoint', 'spend_rank_render_content' );

Still doesn’t work? Oh yeah – don’t forget to re-save your permalinks! From the /wp-admin/ area: Settings -> Permalinks -> Save (no edits) OR perhaps on plugin/feature activation, you could try a flush_rewrite_rules() so your customers don’t have to do that manually.

Bonus: Contextually detecting your new WooCommerce Page

Here’s a quick bonus: what if you need to enqueue a special script or style but you’d rather it only be for your new page. You can use the is_wc_endpoint_url function that comes out of the box with WooCommerce. Check it out:

// Can only be run during hook `parse_request` @ $prio > 10 or after
if ( is_wc_endpoint_url( 'spend-rank' ) ) {
    // enqueue a script? wp_die()? You decide! =D

That’s pretty much it, y’all! Integrating your plugin’s functionality into WooCommerce should be the easy part. Hopefully this gets you a little closer. Now go write the hard part 😉