search ]

How to add WordPress Related Posts Without a Plugin

Showing Related Posts at the bottom of your articles keeps visitors browsing your site longer, improves engagement, and helps with SEO through better internal linking.

Since I always try to keep the number of plugins to a minimum for security and performance reasons, I prefer building features like this with code (same approach I used when adding pagination without a plugin). Here’s how to add related posts to your WordPress site without a plugin, and why it’s worth doing.


Why Add Related Posts at the End of an Article?

Those relevant articles, as their name implies, contain posts that are relevant to the topic of the article the reader is currently reading.

It’s a common feature on most websites and blogs, and for good reason. Here are a few ways it benefits your site:

  • They enhance the user experience – Displaying relevant articles is a non-intrusive and very convenient way for your users to discover articles that are relevant to their interests.
  • They help you rank higher – A crucial part of On-Page SEO and optimization for Google and search engines is the correct Internal Linking strategy. Relevant articles are a part of this strategy, automatically helping you build internal links and assisting Google and various search engines in understanding your site’s structure.
  • They reduce bounce rate – We all try to reduce bounce rates on our sites. Visitors interested in specific content are more likely to click on those links, thereby decreasing the bounce rate and keeping them on your site for a longer time.
  • They provide access to old content – If you frequently add content to your blog, older content quickly gets buried in the later pages that visitors often don’t reach. Adding relevant articles at the end of each post is an excellent way to showcase that hidden content to your visitors.

While these relevant posts are displayed automatically, we’ll ensure that posts specifically tailored to the current post’s content are always shown to the reader.

Determining which posts are relevant to the current post can be done based on categories, tags, or taxonomy in the function outlined below.

Adding Related Posts in WordPress Template

You can add related articles to your template in a few simple steps.

Step One is to add the following code to the functions.php file. If you’re using a child theme, add the code to the functions.php file of your child theme.

<?php

function sv_related_posts($args = array()) {

    global $post;

    // default args
    $args = wp_parse_args($args, array(
        'post_id' => !empty($post) ? $post->ID : '',
        'taxonomy' => 'category',
        'limit' => 3,
        'post_type' => !empty($post) ? $post->post_type : 'post',
        'orderby' => 'rand',
        'order' => 'DESC'
    ));

    // check taxonomy
    if (!taxonomy_exists($args['taxonomy'])) {
        return;
    }

    // post taxonomies
    $taxonomies = wp_get_post_terms($args['post_id'], $args['taxonomy'], array('fields' => 'ids'));

    if (empty($taxonomies)) {
        return;
    }

    // query
    $related_posts = get_posts(array(
        'post__not_in' => (array)$args['post_id'],
        'post_type' => $args['post_type'],
        'tax_query' => array(
            array(
                'taxonomy' => $args['taxonomy'],
                'field' => 'term_id',
                'terms' => $taxonomies
            ),
        ),
        'posts_per_page' => $args['limit'],
        'orderby' => $args['orderby'],
        'order' => $args['order']
    ));

    if (!empty($related_posts)) { ?>
        <div class="related-posts">
            <h3 class="widget-title"><?php _e('Related articles', 'textdomain'); ?></h3>

            <ul class="related-posts-list">
                <?php
                foreach ($related_posts as $post) {
                    setup_postdata($post);
                    ?>
                    <li>
                        <a class="title" href="<?php the_permalink(); ?>" title="<?php the_title_attribute(); ?>">
                            <?php if (has_post_thumbnail()) { ?>
                                <div class="thumb">
                                    <?php echo get_the_post_thumbnail(null, 'medium', array('alt' => the_title_attribute(array('echo' => false)))); ?>
                                </div>
                            <?php } ?>
                            <h4><?php the_title(); ?></h4>
                        </a>
                    </li>
                <?php } ?>
            </ul>
            <div class="clearfix"></div>
        </div>
        <?php
    }

    wp_reset_postdata();
}

Pay attention that if you are interested in using the translation files of the template – you should replace textdomain in line number 48 with textdomain of your template. Otherwise, simply change the text “Related Posts” to whatever you want.

Step Two is to call the function sv_related_posts() wherever you want in your template, usually you’ll want to add it in the single.php file after calling the_content().

This approach works with classic WordPress themes that use PHP template files like single.php. If you’re running a block theme (such as Twenty Twenty-Four), you’ll need to register a custom block or use the the_content filter to inject the output instead.

Performance considerations

One of the reasons we use get_posts() in this function is performance. Unlike a raw WP_Query, get_posts() automatically sets no_found_rows to true and suppress_filters to true by default. That means WordPress skips the extra SQL query needed to calculate pagination, which saves time on every page load.

If your site gets heavy traffic, you can take this a step further and cache the related posts result in a transient so the query only runs once every few hours instead of on every single page view.

[savvy_alert type=”info” title=”Performance Tip”]For high-traffic sites, wrap the get_posts() call in a transient using set_transient() with a 12-hour expiration. This reduces database queries significantly and keeps your page load times low.[/savvy_alert]

Styling the Related Posts section

The HTML output from our function uses the .related-posts and .related-posts-list classes. Here is a minimal CSS snippet you can add to your theme’s stylesheet to create a responsive grid layout:

.related-posts-list {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 20px;
    list-style: none;
    padding: 0;
}

.related-posts-list li {
    overflow: hidden;
}

.related-posts-list .thumb img {
    width: 100%;
    height: auto;
    border-radius: 4px;
}

.related-posts-list h4 {
    margin-top: 8px;
    font-size: 16px;
}

@media (max-width: 768px) {
    .related-posts-list {
        grid-template-columns: 1fr;
    }
}

The arguments you can use

The function uses standard WordPress hooks and patterns, making it versatile. It can be used with several arguments to display relevant content according to the requirements of your WordPress site – or more accurately, your blog.

You can determine the number of posts to be displayed, their order, the taxonomy by which the content will be displayed, and more.

In essence, you can use any argument available for the get_posts() function, since that’s what the code above is built on.

There’s one small difference to keep in mind: to limit the number of displayed posts, use the limit argument. It gets translated to the posts_per_page argument internally.

That’s pretty much it. Let me show you some examples of how to call the function…

Related Posts – Examples of using the function we created

1. Displaying three Related Posts:

<?php sv_related_posts(); ?>

The default arguments of the function is to work by categories, where only 3 posts will be displayed.

If you want to change the number of displayed posts, simply use the limit argument, as in the following example:

<?php
sv_related_posts(array(
    'limit' => 6
));
?>

2. Displaying Related Posts based on tags (which are a taxonomy for everything):

<?php 
sv_related_posts(array(
   'taxonomy' => 'post_tag',
));
?>

3. Displaying six Related Posts by categories, ordered by the number of comments:

<?php 
sv_related_posts(array(
   'limit' => 6,
   'orderby' => 'comment_count',
   'order' => 'ASC'
));
?>

Take a look at additional sorting options in the WordPress Developer Resources.

4. Displaying articles for a custom content type:

As mentioned, you can use this function for Custom Post Types as well. To do so, you need to define and use a taxonomy for that content type.

Without a taxonomy, we have no way to determine which posts in that content type are related to each other.

Nevertheless, here is an example that displays relevant articles from the teams taxonomy:

<?php
sv_related_posts(array(
    'taxonomy' => 'teams'
));
?>

Since in 99% of cases, when we are in a specific post, the relevant articles will be of the same content type, there is no need to specify the content type in the arguments. The function automatically identifies the content type we are in.

FAQs

Common questions about adding related posts without a plugin:

Can I use this with custom post types?
Yes. The function automatically detects the current post type, so it will query related posts of the same type. Just make sure you have a taxonomy (like a custom category or tag) registered for that post type, since the function relies on shared taxonomy terms to find related content.
How do I style the related posts section?
The function outputs HTML with the classes .related-posts and .related-posts-list. You can target these in your theme's CSS. We included a CSS grid snippet earlier in this guide that creates a responsive three-column layout.
Will this slow down my site?
The function uses get_posts(), which sets no_found_rows to true by default, skipping the pagination count query. For most sites, the performance impact is minimal. On high-traffic sites, you can wrap the output in a transient to cache the results.
Can I show related posts by tag instead of category?
Yes. Pass 'taxonomy' => 'post_tag' as an argument. You can actually use any registered taxonomy, including custom ones.
How do I limit related posts to the same taxonomy term?
The function already does this by default. It fetches the taxonomy terms assigned to the current post and queries for other posts sharing those same terms. If a post belongs to multiple categories, the results may include posts from any of those categories.
What's the difference between get_posts() and WP_Query for related posts?
get_posts() is a wrapper around WP_Query with performance-friendly defaults already set. It disables pagination counting and filter suppression out of the box. For a simple related posts query where you don't need pagination or hooks, get_posts() is the better choice.

Conclusion

Adding relevant articles to your blog is a practical way to improve user experience. When visitors see content that matches what they’re already reading, they’re more likely to keep browsing, which means lower bounce rates and more time spent on your site. Combine it with infinite scroll for an even smoother browsing experience.

I use this exact approach on my own blog and it works well. If you have questions or spot a bug in the code, let me know in the comments.

Join the Discussion
0 Comments  ]

Leave a Comment

To add code, use the buttons below. For instance, click the PHP button to insert PHP code within the shortcode. If you notice any typos, please let us know!

Savvy WordPress Development official logo