How to display a primary category on your post excerpts.
When I’m organizing a main blog page, I often like to make the primary category stand out so readers can quickly spot the theme or topic of each post. Meanwhile, on the single post pages, I list all the categories. Here are a couple of examples of this:
I use Yoast SEO on all of my sites, which includes a feature to set a category as the primary category. This does most of the work for me, but I still want to ensure that a primary category displays even if this is not set or if Yoast SEO is disabled.
To achieve this, I’ve created a function called get_primary_category
. This function takes a WordPress post ID and returns an HTML string with a link to the primary or first category of the post. I use name spacing in my theme, but if you don’t, you’ll want to add a prefix to your function name. Here is the full function:
function get_primary_category($postid): string {
static $yoast_active = null;
$result = '';
$primary_term_id = '';
// Check Yoast SEO plugin status once and cache it
if ($yoast_active === null) {
$yoast_active = is_plugin_active('wordpress-seo/wp-seo.php') || is_plugin_active('wordpress-seo-premium/wp-seo-premium.php');
}
// Retrieve the primary category if Yoast is active
if ($yoast_active) {
$primary_term_id = yoast_get_primary_term_id('category', $postid);
}
// If no primary category from Yoast, get the first category assigned to the post
if (empty($primary_term_id)) {
$categories = wp_get_post_terms($postid, 'category');
if (!is_wp_error($categories) && !empty($categories)) {
$primary_term_id = $categories[0]->term_id;
}
}
// If a category ID was found, fetch and format the category link
if ($primary_term_id) {
$term = get_term($primary_term_id, 'category');
if (!is_wp_error($term)) {
$url = get_term_link($term);
if (!is_wp_error($url)) {
$name = esc_html($term->name);
$result = '<a href="' . esc_url($url) . '">' . $name . '</a>';
}
}
}
return $result;
}
Building the function
First, I create a static variable and set that to null. Additionally, I also set my other variables to an empty string.
static $yoast_active = null;
$result = '';
$primary_term_id = '';
Next, I check whether Yoast SEO or Yoast SEO Premium is active and set that result to my static variable.
if ($yoast_active === null) {
$yoast_active = is_plugin_active('wordpress-seo/wp-seo.php') || is_plugin_active('wordpress-seo-premium/wp-seo-premium.php');
}
By setting this to a static
variable, it retains its value between function calls. Therefore, the check for plugin activation only occurs once during the lifetime of the script.
The condition if ($yoast_active === null)
checks if $yoast_active
has not been set yet. This check is only true
the first time the function is called. However, in subsequent calls within the same request, $yoast_active
will already hold a value (true
or false
), and this block will be skipped.
Next, I want to check if Yoast SEO is active. If it is active, we want to check if a primary category has been set. If it has been set, it will be assigned to $primary_term_id
.
if ($yoast_active) {
$primary_term_id = yoast_get_primary_term_id('category', $postid);
}
If no primary category is found, or if Yoast SEO isn’t active, we fall back to using the first category associated with the post. To do this, we get all the categories from the post using wp_get_post_terms()
. We want to check both that this function does not return an error and that it doesn’t return an empty array. Then, we can set the $primary_term_id
to the first category in the array.
if (empty($primary_term_id)) {
$categories = wp_get_post_terms($postid, 'category');
if (!is_wp_error($categories) && !empty($categories)) {
$primary_term_id = $categories[0]->term_id;
}
}
With the $primary_term_id
, we can use it to get the category object using get_term()
. Now we can get the name of the category and the url to the category archive page. Now, we construct our link and set it to the $result
variable.
if ($primary_term_id) {
$term = get_term($primary_term_id, 'category');
if (!is_wp_error($term)) {
$url = get_term_link($term);
if (!is_wp_error($url)) {
$name = esc_html($term->name);
$result = '<a href="' . esc_url($url) . '">' . $name . '</a>';
}
}
}
Finally, we return our link with the string.
return $result;