When re-designing this site, I decided early to stay with a more traditional two-column layout for blog posts. Full-width has been more the trend lately, and certainly has advantages, but I liked the idea of presenting the post and meta-data side-by-side this way. Because the bottom of posts tends to be busy with sharing links, comment forms, and what have you, I liked the idea of moving information that I wanted to be more accessible into the sidebar. This included my post metadata, as well as related posts. Both are custom widgets, and I’ll save the metadata for another time. Here, we’re going to talk about building related posts into your sidebar in your custom theme or child theme for WordPress.
But, isn’t there a plugin for that?
Sure. Actually, there are a lot of them. Generally, they come with some opinionated display options that may or may not inherit correctly from the rest of the site, and may or may not incorporate widgets. Frequently they involve more complex logic to guess related posts for you, and in general are heavy. If you’re looking to add this feature without a lot of overhead, or without code, then absolutely, one of those options might be a good choice for you. My approach is for custom sites, because that’s mostly what I build, and would also work in a child theme. In these cases, adherence to design and branding is key, and a clean user experience is important, especially if multiple editors are involved. Also, if you’re the DIY type, this is a fun project that will only take an hour or two to build.
When I designed this feature, I knew that I wanted a maximum of three related posts, and that I wanted to curate them by hand, even if this meant some posts would have them and some wouldn’t. I use Advanced Custom Fields (ACF) in every WordPress site that I build, so we’ll be using that to add the related posts field to posts. If you don’t have a subscription to ACF, it’s worth every penny, and a purchase that you really should make. Like, now. If you really want to geek out and have a ton of other useful options at your fingertips, I also recommend ACF Extended. You may see some options in the screenshots here that are added by ACF Extended, and not available only with ACF.
So, without further explanation, let’s dive in!
Building the fields
If you’re not familiar with ACF, take a quick look at the the documentation for creating field groups. We’re going to create a field group called, you guessed it, Related Posts. You’ll start this under Custom Fields > Add New. Now, let’s add a single field to this group, which we’ll also call Related Posts, and select Relationship for the field type. The relationship field does exactly what it says on the tin: it allows us to create a relationship between our current post and other posts. Because I want flexibility to add this or not for each post, I’m not going to make it a required field.
Next, we’ll select how we want to filter. This controls which post types on your site will be available to this field for creating relationships to this post. I’m going to permit filtering here only for posts, because that’s the nature of related posts. This will vary widely depending on what post types you have on your site, and the specifics of what you want to be able to relate to each post (such as product types on an ecommerce storefront, for example). There are no constraints here, so customize however you like.
I’m going to set the minimum posts to 1, and maximum to 3, to fit my design. Again, these values can be whatever you like to match your site. I’m including Featured Image under Elements, as I want the thumbnail for each post displayed on the front end. For Return Format, we can select what data from the post we’re relating to that we want to have available as the output. Post Object returns an object with all of the data for the post in question, and Post ID returns only the ID for that post. We’re going to select Post Object, as we’ll need access to multiple aspects of each post for this project, but you could easily just use ID depending on how you want to write your code.
For Location, choose Post Type is equal to Post, which will place your field group on all posts. Adding a description to all of your field groups makes for an easier administrative experience. Most of the rest of the fields here can remain at their defaults, though you may want adjust the Order No. field if you have additional field groups on your posts and want the groups to present in a specific order.
Save, and that’s it. Now, when you edit a post, you’ll have a field available listing other posts (or entries of additional post types, depending on whether or not you changed this option when creating the field) on the left. Clicking one adds that post to the right, up to the limit that you set when creating the field.
Creating the widget
Now we get to roll up our sleeves and write some code. If you haven’t, take some time to understand widgets and how they work in WordPress. I know the future of widgets is uncertain as the block editor continues it’s evolution, but for now we have them, we use them, and they remain one of the most flexible aspects of WordPress as it exists now.
Creating a widget involves creating a new class. A class is a concept of object oriented programming that allows for accessing core APIs without having to re-invent the wheel each time. In your custom theme (or child theme), you’ll want to create a directory called classes. You can see an example in the structure of the Twenty Twenty theme. In this directory, we’re going to create a file called
class-related-posts-widget.php. Naming the file with “class-” at the beginning is a PSR naming convention. Here’s what the file looks like, and I’ll walk us through each aspect of it below.
First, we’re creating a class called
Related_Posts_Widget, and this class extends the
WP_Widget class from core. This means that it has access to all of the components of the base class, which is everything we need to build a widget. Our
__construct function creates the new widget instance, including the unique ID, CSS class and description. Note that, for all
esc_html__() or similar functions in this example, ‘mytheme’ as the second argument will need to be changed to the handle of your custom theme for proper translatability.
The next function,
widget, builds the front-end output of the widget. We’re accessing the ID of the queried post (where the widget will be appearing) in the
$current_post variable, because we’ll need to pass that to the
get_field function. This is really important, as it solves the problem of how ACF will know which post contextually that it needs to get the field data from for our related posts field. Normally, a widget would not be aware of this. Also, note that I’m wrapping all of this in a
function_exists check for a function included by ACF. This ensures that that this code will fail gracefully if the ACF plugin were to be deactivated, thus avoiding a white screen of death due to an undefined function error, as functions like
the_field would not exist in that scenario. We’re setting the value of the related posts field to the
$related_posts variable, then checking to see if the field is empty, and returning if it is. This will prevent the widget from displaying on posts where the field hasn’t been completed, even when the widget is added to the sidebar globally. The rest of this function is fairly self-explanatory: we’re just building an unordered list of each post’s title, linked to the post URL, and featured image. You’ll notice an
$args array here. That’s being provided by the base class that we extended in our construct function, and gives us access to things like the markup surrounding the widget, the widget title, etc., some of which is defined in your theme’s functions.php file. Also, for the thumbnail, you’ll see we’re calling a “widget” image size. This is a custom image size that I defined for this widget. You can see how to define custom image sizes in WordPress here. You can easily just substitute this for “thumbnail” or whichever native size you prefer if you don’t want to do this.
form function defines the backend form for the widget that we’ll see either in the customizer, or under Appearance > Widgets when we add the widget to a sidebar. The only option we’re allowing for here is the widget title, so that the user can give the widget whatever title they like to display on the front end. If left empty, it will default to “Related Posts.” The last function here,
update, is sanitizing the input of the widget title field for security, using a native core function.
All that’s left to do now is to go our functions.php file, require our new class, and register the widget that it provides. Here’s an example of what to add to your functions.php to accomplish this:
And there you have it. You can now place your shiny new widget into your widget area, and it will display the contents of your related posts field on each post if the field has content. We’ve provided the list of related posts with a ‘related-posts-list’ class, each list item with a ‘related-post-item’ class, and the widget itself with a ‘related_posts_widget’ class for more granular styling if you need to do so. You can see the end result in the sidebar of posts right here on this blog.
I hope this adds another tool to your toolkit for your projects. Need some help, or have an idea on how to expand on this in your site? Get in touch. I’d love to chat or help out.