Adding a related content section on a blog can increase user engagement and reduce bounce rates. There are tons of plugins available for WordPress to display related posts around the content. In this tutorial, I'll show you how to create a customized related posts section without using any plugins. Our related posts section will include support for thumbnails, excerpts, and category-based filtering. Grid layout will be used to display them on all kinds of devices. Advanced users can use the example given below as a base to add more features. Let's get started and code a customized related posts section.

Always test your plugin-less solution on a live replica or a staging site forked from your main website. This way, you'll be able to thoroughly test and tweak it before shifting it to the production environment.
With the advent of caching technologies and the use of CDN, related posts don't put any burden on shared server resources. So, feel free to use a custom solution instead of picking a plugin for the same.
Features and Attributes of Our Custom Solution
Before we dive into the code, let's quickly go through the features and functionalities we'll incorporate in our related posts section. It'll help us define the scope of the task at hand.
- Primary Function: We'll create a main function that contains the core backend logic to fetch and display related posts. There's a lot of scope to enhance and improve this function as per your needs.
- Grid Layout: A CSS grid layout will be used for a responsive section. The CSS rules will be created to ensure the related post section is displayed correctly on desktop, tablet, and mobile devices.
- Customizable Options: Through arguments to the main function, the user can tweak or change the output. For example, the number of entries and thumbnail display.
- Clean Code: The focus will be to keep the code minimal and optimized so that it doesn't put a load on the server resources. If you are using caching correctly, it'll help.
Step 1: Add the Main Function
As mentioned in the previous section, our primary function is where all the core logic will reside. So, let's first create this function. To do that, open the functions.php
file of your current theme. I'll recommend using a child theme and using its function.php
file.
Here's the function to display related posts:
function display_related_posts($post_count = 3, $show_thumbnail = true) {
// Get the current post's categories
$categories = get_the_category();
if ($categories) {
// Extract category IDs
$category_ids = array();
foreach ($categories as $category) {
$category_ids[] = $category->;term_id;
}
// Set up the query arguments
$args = array(
'category__in' => $category_ids,
'post__not_in' => array(get_the_ID()),
'posts_per_page' => $post_count,
'orderby' => 'rand',
'no_found_rows' => true,
'ignore_sticky_posts' => true
);
// Run the query
$related_query = new WP_Query($args);
if ($related_query->have_posts()) {
echo '<div class="related-posts">';
echo '<h3>Related Posts</h3>';
echo '<div class="related-posts-grid">';
while ($related_query->have_posts()) {
$related_query->the_post();
?>
<article class="related-post">
<?php if ($show_thumbnail && has_post_thumbnail()): ?>
<a href="<?php the_permalink(); ?>" class="related-thumb">
<?php the_post_thumbnail('medium'); ?>
</a>
<?php endif; ?>
<h4 class="related-title">
<a href="<?php the_permalink(); ?>">
<?php the_title(); ?>
</a>
</h4>
<div class="related-excerpt">
<?php echo wp_trim_words(get_the_excerpt(), 20); ?>
</div>
</article>
<?php
}
echo '</div></div>';
}
wp_reset_postdata();
}
}
You can see that the thumbnail support is enabled by default which can be overridden by passing false
as the second parameter to the function. Similarly, up to 3 related posts will be displayed by default. You can override this value through the first parameter of the function.
Step 2: Add the Styling
Now that the core function is complete, we'll add another one to apply CSS styles to the HTML markup we'll be using for the related posts section. Here we go!
The following function adds the CSS rules to the related post section:
function add_related_posts_styles() {
?>
<style>
.related-posts {
margin: 2em 0;
padding: 1em;
border-top: 1px solid #ddd;
}
.related-posts h3 {
margin-bottom: 1em;
}
.related-posts-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5em;
}
.related-post {
display: flex;
flex-direction: column;
}
.related-thumb img {
width: 100%;
height: auto;
margin-bottom: 0.5em;
}
.related-title {
margin: 0.5em 0;
}
.related-excerpt {
font-size: 0.9em;
color: #666;
}
</style>
<?php
}
add_action('wp_head', 'add_related_posts_styles');
Feel free to tweak and change these CSS rules to match your requirements. You can even switch to Flexbox if you prefer that, though I'll recommend sticking with the grid system.
Step 3: Display Related Posts
The last step is all about calling the main function at the right place within your theme. Usually, this place is a single.php
file. Right after the post content, you can call the main function as shown below.
<?php
if (is_single()) {
display_related_posts();
}
?>
As you can see we have not passed any parameter to this function. It means, a maximum of 3 related posts will be shown and thumbnails will be displayed, if available.
Customization Options
Let's quickly look at the customization we have to tweak or change the output of our related posts section. Most of them are easy to exercise.
Changing the Number of Posts
By default, 3 posts are shown. But, if you want to show more, call the main function as follows:
// Show 6 related posts
display_related_posts(6);
Disabling Thumbnails
If you have a minimal and text-heavy theme and want to exclude thumbnails in the related posts section, call the function as follows:
// Show 4 posts without thumbnails
display_related_posts(4, false);
Styling Modifications
If you want to customize the appearance of the related posts section, you have to directly edit the CSS rules in the add_related_posts_styles
function. For example, if you want a 3-column grid layout, modify the CSS rules as follows:
.related-posts-grid {
grid-template-columns: repeat(3, 1fr);
}
If you are a CSS pro, the possibilities of customizing appearance and layout are unlimited. Feel free to use a completely different set of CSS rules to gel it well with your theme.
Enhancing With Additional Features and Customizations
If you are happy with this basic implementation, you can skip the rest of the tutorial. But, if you want to add a few more features, take a look at the set of features we'll add to our base implementation.
- Date Display: Apart from thumbnail, title, and excerpt, we'll also add the post date for each entry.
- Tag-based Posts: Instead of categories, we'll fetch the posts based on tags. It'll give us much more diverse yet related posts.
- Custom Sorting: At present, the fetched posts are ordered randomly in the grid. But now, we'll add a couple of sorting options.
- Results Caching We'll cache the results for a fixed time interval to reduce the load on the server resources.
- Animation Effects: A few CSS animations effects will be added to make the related posts section—more appealing.
Modify Main Function
Let's see the enhanced version of the main function.
function display_related_posts($post_count = 3, $show_thumbnail = true, $orderby = 'date') {
// Get current post's tags
$tags = get_the_tags();
if (!$tags) return;
$tag_ids = array();
foreach ($tags as $tag) {
$tag_ids[] = $tag->term_id;
}
// Generate a unique cache key
$cache_key = 'related_posts_' . get_the_ID() . '_' . md5(serialize([$post_count, $orderby, $tag_ids]));
// Try to get cached results
$related_posts = get_transient($cache_key);
if (false === $related_posts) {
$args = array(
'tag__in' => $tag_ids,
'post__not_in' => array(get_the_ID()),
'posts_per_page' => $post_count,
'orderby' => $orderby,
'no_found_rows' => true,
'ignore_sticky_posts' => true
);
// Handle different sorting options
switch ($orderby) {
case 'popularity':
$args['orderby'] = 'comment_count';
break;
case 'rand':
$args['orderby'] = 'rand';
break;
default:
$args['orderby'] = 'date';
}
$related_query = new WP_Query($args);
$related_posts = $related_query->posts;
// Cache for 24 hours
set_transient($cache_key, $related_posts, 24 * HOUR_IN_SECONDS);
}
if ($related_posts) {
echo '<div class="related-posts">';
echo '<h3>Related Posts</h3>';
echo '<div class="related-posts-grid">';
foreach ($related_posts as $index => $post) {
setup_postdata($post);
?>
<article class="related-post animate-item" style="--i: <?php echo $index; ?>">
<?php if ($show_thumbnail && has_post_thumbnail()): ?>
<a href="<?php the_permalink(); ?>" class="related-thumb">
<?php the_post_thumbnail('medium'); ?>
</a>
<?php endif; ?>
<div class="related-meta">
<time datetime="<?php echo get_the_date('c'); ?>" class="related-date">
<?php echo get_the_date(); ?>
</time>
<h4 class="related-title">
<a href="<?php the_permalink(); ?>">
<?php the_title(); ?>
</a>
</h4>
</div>
<div class="related-excerpt">
<?php echo wp_trim_words(get_the_excerpt(), 20); ?>
</div>
</article>
<?php
}
echo '</div></div>';
wp_reset_postdata();
}
}
Let's see what modification has been done in this new enhanced function.
First, a third parameter has been added that dictates the sorting criteria. The default sorting is done on a date, but you can sort based on comment count and random order too. The arguments you can use are date
, popularity
, and rand
which are available as the sorting options.
Date has been added to each entry using the <time>
HTML element.
With the help of tag__in
, posts are fetched based on tags which gives you a much more diverse set of related posts.
Last but not least, caching has been implemented using the WordPress transients API with a caching time of up to 24 hours. Any post update automatically clears the cache.
Modify and Enhance Styling
Now that the core function has been modified, it's time to modify and add CSS rules to accommodate the new changes. To do that, modify the add_related_posts_styles
function. Here's the code!
function add_related_posts_styles() {
?>
<style>
.related-posts {
margin: 2em 0;
padding: 1em;
border-top: 1px solid #ddd;
}
.related-posts-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5em;
}
.related-post {
opacity: 0;
transform: translateY(20px);
animation: slideUp 0.5s ease forwards;
animation-delay: calc(0.1s * var(--i));
background: #fff;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
@keyframes slideUp {
to {
opacity: 1;
transform: translateY(0);
}
}
.related-thumb img {
width: 100%;
height: 200px;
object-fit: cover;
}
.related-meta {
padding: 1em;
}
.related-date {
display: block;
font-size: 0.8em;
color: #666;
margin-bottom: 0.5em;
}
</style>
<?php
}
add_action('wp_head', 'add_related_posts_styles');
In this new version, we've added a class to stylize the date. Animation effects for each post entry have been added too. You can change or tweak them as per your needs.
Add Cache Clearing Mechanism
In this new and enhanced version, you have to add one more function to the functions.php
file. It's for handling the caching mechanism.
// Clear cache on post-save
function clear_related_posts_cache($post_id) {
if (wp_is_post_revision($post_id)) return;
delete_transient('related_posts_' . $post_id . '_*');
}
add_action('save_post', 'clear_related_posts_cache');
The function mentioned above clears the cache every time the post is updated. This ensures you do not fetch old and stale versions of these posts. It's a must-have feature for every related post section.
Usage Examples
And now let's see some examples of using this new and improved related posts section. It'll give you a clear idea about the parameters you can use to customize the output.
// Sort posts on popularity with thumbnail support
display_related_posts(3, true, 'popularity');
// Randomly pick 4 posts with thumbnail support
display_related_posts(4, true, 'rand');
// Fetch 6 posts sorted on a date without thumbnail support
display_related_posts(6, false, 'date');
All three parameters of the main function give you enough flexibility to change the output as per your needs. You can also call this function without any arguments to fall back to the default options.
Conclusion
Although one can easily add related posts section on WordPress through a plugin, creating a custom solution gives us complete control over its customization. If you have a working knowledge of HTML, CSS, and PHP, go for a pluginless solution to create a unique version of related posts that completely match with theme's design and layout.