Yoast WP Primary Category - Make Primary

The 3.1 update to Yoast’s WordPress SEO plugin brought with it a new feature—the ability to set a “Primary” category for a post. You’ll now see a “Make Primary” link when selecting categories (or a bold “Primary” label if one has been set). Some people may confuse this for a native WordPress feature. Which, arguably it should be!

The WordPress SEO plugin uses this when generating its optional breadcrumb links. So the most important category can appear, rather than one of many categories that may be applied to a post.

But what about using this in a custom WordPress theme? We can do that.

February 2022 Update:
I just updated the script with a new an improved version, since this has been pretty popular and it needed a few adjustments. The primary meta key is now retrieved via the post meta value, which is a little more futureproof. It’s now a function with a parameter for showing the link or not. And both the URL and category name are now escaped with the WP functions _esc_url_ and _esc_html_e_ (displays symbols like “&” properly as discussed in some of the comments).

Display the Primary Category, if it’s Available

On the site I used this for, I wanted to display a single category along with the post, on the post archive pages. The design was such that showing multiple categories was unwanted and would take up too much space. Using the_category() function will show all categories assigned to a post.

The following code will display Yoast’s primary category if it’s available, otherwise it will fallback to displaying the first category returned by get_the_category() (I think that this is the first category that has been assigned).

Note: It is possible that Yoast may change the way that it stores the primary category in the future. For now we are relying on this being stored in the post meta “_yoast_wpseo_primary_category”.

I’ve also added an option $useCatLink which you can set to false if you do not want the categories to be linked.

Comments on this Article

  1. Olivier says:

    Hi, thank you for this piece of code. I have used it successfully on my theme to retrieve the Primary category.

  2. Jimmy says:

    Thank you for the writeup. Unfortunately this didn’t work for me – the constructor would only return an empty object.

    I was able to get around it by looking up the post_meta in the database (called _yoast_wpseo_primary_category) and using get_term() to get it there.

  3. Karen says:

    Have you figured out a way to get next-previous post links based on primary category?

  4. Conrad says:

    hello i wanna display primary cat. and other 2 cat. too. how can i set display limit?

  5. Julie says:

    Brilliant! I was able to use it without modification. Thank you so much!

  6. Karen says:

    Very helpful – thank you!

  7. Sara says:

    Ace!!! Thank you so much! Works perfectly!

  8. Rog says:

    Thanks for sharing this! I am hoping to get it working, but seem to have encountered a roadblock.

    I am having some trouble with implementation, and was wondering if you had any thoughts on why that might be.

    if (is_wp_error($term)) {
    // Default to first category (not Yoast) if an error is returned
    } else {
    // Yoast Primary category
    …always results in an error, and therefore only ever returns the default first category, never the Yoast primary category. Forcing the post to run the “} else {” statement (e.g. by removing the if/else statement and leaving “//Yoast Primary category….”) results in the following php error:

    Notice: Undefined property: WP_Error::$name in…[line 22]
    Line 22 reads “$category_display = $term->name;”

    To what should “name” refer in this instance? It must be defined when the “if (is_wp_error($term))” statement is run, because no error occurs. So why is it undefined in the “} else {” statement? What am I missing.

    Any thoughts on what’s going on here?

    • Rog says:

      So I consider this a valuable lesson in web design. When something doesn’t work in your design, there are essentially five things that could have gone wrong:

      1) The code could be flawed.
      2) The platform (wordpress, a theme, or a plugin) could be causing problems.
      3) The web browser you are using could be interacting with the code badly.
      4) The end-user could be breaking something somehow.
      5) And this one is probably the very most important…. sometimes the problem is caused by something stupidly simply you’ve overlooked.

      I was able to solve the problem by double-checking that fifth possibility. This code snippet will always return the first category, because the first category is the primary category by default when only one category is selected. And if you think that means what you think it does, you would be right.

      I forgot to select a second category for the post I was using to test this piece of code. The code was doing exactly what it was supposed to. As soon as I remembered to select more than one category and set one of them as primary, it gave me the desired result. So I spent hours trying to solve a coding problem which didn’t exist.

      And that is the valuable lesson to take away from the problem I described in my initial comment. When you’re writing code and it doesn’t behave as expected, look for the stupid simple oversight on your own part before you decide that the code itself is broken. It doesn’t matter how long you’ve been doing this kind of work–you will forget this lesson from time to time.

  9. Brendan says:

    Would love to implement this! Where does this code go? Just in the individual files?

    • Josh says:

      You could make a function out of it and add to your functions.php (ideally in a child theme if possible, to avoid modifications to the base theme that you are using). Then call that function in the template files where it’s needed. It really depends on the theme.

      • Tom says:

        Anybody here able to make a function out of this? I tried but it didnt work.
        Need it to hook into woocommerce_after_shop_loop_item_title

        This is what I tried:

        add_action( ‘woocommerce_after_shop_loop_item_title’, ‘getparentcat’, 1);
        function getparentcat() {
        $category = get_the_category();
        $useCatLink = true;
        // If post has a category assigned.
        if ($category){
        $category_display = ”;
        $category_link = ”;
        if ( class_exists(‘WPSEO_Primary_Term’) )
        // Show the post’s ‘Primary’ category, if this Yoast feature is available, & one is set
        $wpseo_primary_term = new WPSEO_Primary_Term( ‘category’, get_the_id() );
        $wpseo_primary_term = $wpseo_primary_term->get_primary_term();
        $term = get_term( $wpseo_primary_term );
        if (is_wp_error($term)) {
        // Default to first category (not Yoast) if an error is returned
        $category_display = $category[0]->name;
        $category_link = get_category_link( $category[0]->term_id );
        } else {
        // Yoast Primary category
        $category_display = $term->name;
        $category_link = get_category_link( $term->term_id );
        else {
        // Default, display the first category in WP’s list of assigned categories
        $category_display = $category[0]->name;
        $category_link = get_category_link( $category[0]->term_id );
        // Display category
        if ( !empty($category_display) ){
        if ( $useCatLink == true && !empty($category_link) ){
        echo ”;
        echo ‘‘.htmlspecialchars($category_display).’‘;
        echo ”;
        } else {
        echo ”.htmlspecialchars($category_display).”;


  10. Roee yossef says:

    Thanks you, works great!

  11. Anas says:

    how to display a parent category of the “Primary” Category??

  12. Karina says:

    Hi! Is there a way to give a primary category a special class when using wp_list_categories function?

  13. Izabela says:

    Hi there! Thank you for this. Where do I place this code?

  14. Killer Designer says:

    Is there a way to disable this feature?

  15. Tony says:

    Hello! First off thank you for the code! I am trying to make the $category_display Variable the taxonimy of the get_previous_post() and get_next_post() functions. This is because I have multiple categories on items and when I cycle through them the loop will jump to the others.

  16. Dr John Joss Chinn says:

    Yoast seems to have messed up my category structure a treat. If I look at a particular post, which I put in a sub-category, then it says that that sub-category is actually a primary category. If I then go to Categories, in WP, in order to correct this, then it actually shows the sub-category as being under the main category anyway, so I can’t correct it as it looks correct there. I can probably re-create sub-categories and move the posts around. Well, this is what I will do. But what a pain, and there is no guarantee that when there is an update to Yoast or whatever, it won’t re-mess it up.
    Thanks for your post on the subject by the way, most appreciated.

  17. Felix says:

    Thanks for the code!

    I made another version that works in the themes functions.php and uses the filter “get_the_categories” to modify the $categories array so that the primary category will be the first one in the array.

    To get the primary category in the theme I can just use the get_the_category()-function like so: $categories = get_the_category(); $primary_cat = $categories[0];

    Checks for a yoast primary category, if it exists move the category to the first position in the $categories array.
    function yoast_primary_cat_as_first_cat($categories) {
        // Check if yoast exists and get the primary category
        if ($categories && class_exists('WPSEO_Primary_Term') ) {
            // Show the post's 'Primary' category, if this Yoast feature is available, & one is set
            $wpseo_primary_term = new WPSEO_Primary_Term( 'category', get_the_id() );
            $wpseo_primary_term = $wpseo_primary_term->get_primary_term();
            $term = get_term( $wpseo_primary_term );
            // If no error is returned, get primary yoast term 
            $primary_cat_term_id = (!is_wp_error($term)) ? $term->term_id : null;
            // Loop all categories
            if($primary_cat_term_id !== null) {
                foreach ($categories as $i => $category) {
                    // Move the primary category to the top of the array
                    if($category->term_id === $primary_cat_term_id) {
                        $out = array_splice($categories, $i, 1);
                        array_splice($categories, 0, 0, $out);
        return $categories;
    add_filter( 'get_the_categories', 'yoast_primary_cat_as_first_cat' );
    • Arti dogra says:

      Hello everyone,

      I have one query, how i can hide only primary category in frontend using DIVI theme.

      Mrs Arti

    • Edgar says:

      This is great! Is there a way to just show the Primary Category? One category instead of all. Thanks!

  18. Christopher Krohn says:

    Worked great for me! Thanks for this!

  19. Philipp says:

    Works great and is exactly what I needed. Thank you!

  20. Sam says:

    Rather than having to go through the trouble of implementing Yoast’s class, it’s much simpler to get the primary category by: $yoast_primary_key = get_post_meta( get_the_id(), '_yoast_wpseo_primary_category', TRUE );

    Simply returns false if it doesn’t exist.

    • Josh says:

      Thanks for this. That’s a bit more futureproof, as I think the post meta name is less likely to be changed as compared to the class name. I’ve updated the script to use this method.

  21. Christina says:

    This code is working perfectly since the latest Yoast update that broke the old code I had. However, it’s returning html instead of characters, like & instead of & . Any ideas?

    • Josh says:

      I’ve updated the script with a fix for this and some other things (there was some discussion about it on the gist comments as well). Basically, htmlspecialchars in the original was replaced with esc_html_e

Leave a Reply

You can use the <pre> tag to post a block of code, or <code> to highlight code within text.