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.

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 that this code isn’t officially provided by Yoast. I figured it out by digging around on the plugin’s GitHub repository. So it is possible that their API / functions could change in future versions and cause something to break. So just be aware of this before integrating into your custom WordPress theme:

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.

    This…
    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() {
        // SHOW YOAST PRIMARY CATEGORY, OR FIRST CATEGORY
        $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);
    
                        break;
                    }
                }
            }
        } 
        
        return $categories;
    }
    add_filter( 'get_the_categories', 'yoast_primary_cat_as_first_cat' );
    

Leave a Reply

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