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
textdomainof 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 thethe_contentfilter 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:
.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.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.'taxonomy' => 'post_tag' as an argument. You can actually use any registered taxonomy, including custom ones.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.

