Manually Curated Related Posts

There’s a bunch of plugins to automatically generate related posts. But some people prefer to have more control over their related posts and manually select them. This can be set up pretty easily using the Posts 2 Posts plugin.

There’s three pieces of code we’ll add to our core functionality plugin 1:

  1. Register the connection. This adds the metaboxes for linking posts.
  2. Pre-Loop code. This adds the related posts information to the $wp_query (better performance)
  3. Display Related Posts.

Register the Connection

* Related Post Connection
* @author Bill Erickson
* @link
* @uses Posts2Posts
* @link
function be_post_type_connections() {
p2p_register_connection_type( array(
'name' => 'related-articles', // unique name
'from' => 'post',
'to' => 'post',
'title' => array( 'to' => 'All Connections', 'from' => 'Related Articles' )
) );
add_action( 'p2p_init', 'be_post_type_connections' );
view raw gistfile1.php hosted with ❤ by GitHub

I’m creating this connection from the post post type, to the post post type. So when you’re editing a post, you can select other posts to connect it to. This actually creates two metaboxes: a from metabox that you use to connect the current post to others, and a to metabox that shows what posts have already been connected to this one. I’ve labeled the from metabox “Related Articles” since this is what is displayed as related to the current post, and I labeled to “All Connections”.

Once you add this code to your site, you should see metaboxes like this when editing a post:

Pre-Loop Code

* Related Posts Before Loop
* Adds connection data to $wp_query. Run before the loop.
* @author Bill Erickson
* @link
function be_related_posts_pre_loop() {
// Make Sure plugin is active
if ( !function_exists( 'p2p_register_connection_type' ) )
global $wp_query;
p2p_type( 'related-articles' )->each_connected( $wp_query );
view raw hosted with ❤ by GitHub

As described in the Posts 2 Posts wiki, when dealing with archive pages its much better to add the connected posts to the $wp_query before you run the loop.

Display Related Posts

* Display Related Posts
* @author Bill Erickson
* @link
function be_related_posts() {
// Make Sure plugin is active
if ( !function_exists( 'p2p_register_connection_type' ) )
global $post;
if( isset( $post->connected ) && !empty( $post->connected ) ):
echo '<div class="related-posts">';
$count = 1;
foreach( $post->connected as $related ):
if( $count < 6 ) {
echo '<div class="related-post">';
echo '<a class="image" href="' . get_permalink( $related->ID ) . '">';
$cat = wp_get_object_terms( $related->ID, 'category', array( 'count' => 1 ) );
echo '<span class="category">' . $cat[0]->name . '</span>';
echo get_the_post_thumbnail( $related->ID, 'be_home_small' );
echo '</a>';
echo '<a class="title" href="' . get_permalink( $related->ID ) . '">' . $related->post_title . '</a>';
echo '</div>';
echo '</div>';
view raw hosted with ❤ by GitHub

You can of course display the related posts however you’d like. For my specific implementation, I wanted to limit it to 5, display the post image at a specific size, float the category name on top of the post image, and then display the title below the image.

The important thing to note is that $post->connected is an array of post objects, so use the foreach() to loop through them and then display the post data however you’d like.

Integrating it with your theme

Once you’ve added this code to your core functionality plugin, you have to actually call it in your theme. Just place be_related_posts_pre_loop() before you call the loop, and be_related_posts() somewhere inside the loop. If you’re using Genesis, it has hooks throughout the theme, so you just need to add the following two lines to your theme’s functions.php file (alter the second line to change the positioning of the related posts):

// Related Posts (see mu-plugins/lib/functions/related-posts.php
add_action( 'genesis_before_loop', 'be_related_posts_pre_loop' );
add_action( 'genesis_after_post_content', 'be_related_posts', 15 );
view raw hosted with ❤ by GitHub

I’d love to hear any thoughts you have about this, especially if you have experience with Posts 2 Posts.

  1. Core Functionality Plugin
    While you could put all this code in your theme’s functions.php file, a core functionality plugin is better for things like this because if you change your theme in the future, you’ll want to easily move this functionality to your new theme. If it’s in a plugin, all you have to do is add two lines to your theme (call the pre-loop code and then the display code). If all the code is in your old theme, you have to dig through all your files, figuring out how you built your related posts section so you can duplicate it in your new theme. In my core functionality plugin, I created /lib/functions/related-posts.php so that all the code was on one location.


Receive New Posts by Email


  1. I look forward to someday staring at this for a long time and understanding how to implement it. You write really good tutorials – not just showing what to do, but explaining why you’re doing it. Am I missing it, or is there not an option on here to subscribe to your blog via email?

    (I see the “Notify me of new posts by email” checkbox…maybe I’ll just do that.)

    (So I guess that was a dumb question.)

  2. Great tutorial Bill,
    Does it retreive only the featured image though for a post? I know that many of the themes out of convenience retreive the first image from the post if there are no featured images – I assume that it would not load anything if this were the case.

    Bookmarked and saved, will implement this once I get the number of posts up on my blog 🙂

    1. Yes, this code retrieves the featured image. If you’re building on top of Genesis, you can use the genesis_get_image() function which grabs the featured image, and if there isn’t one it pulls the first image in the post.

      But for a tutorial like this there’s no need to complicate things by writing the code that does that when the focus is on the related query.

  3. Thank you! 9/10 times I google for something I need to figure out how to do related to WP or Genesis I find one of your tutorials that EXACTLY covers what I need.

    Today the google query that got me here was: “posts to posts” plugin show featured image. While I knew how to output a mini loop of featured posts I was hoping for an example of how to output the post’s featured image as part of that loop… Bingo.

  4. Hey Bill – this is IMMENSE. Thank you lots!

    Quick question: What kind of CSS would I need to use to have my Related Posts displayed as yours are in the image at the top of this post? Right now the images are the full width of the body content and the titles are unstyled.

    Thanks for a GREAT display of knowledge.


    1. First you’ll need to define the image size. In the code above I’m using ‘be_home_small’, so add this to your theme’s functions.php file: add_image_size( ‘be_home_small’, 150, 100, true );

      Then add some CSS like this (I’m eyeballing all this so you’ll most likely need to tweak it:

  5. Hi Bill,
    Thanks for your continuing tutorials.
    I have a couple of queries on this;
    Is it possible to display custom fields.
    I have tried “echo get_post_meta( $related->ID, ‘name of custom field’ ) ;”
    which gives the result “array.”
    I notice the post to posts plugin gives us a widget which lists related post titles. Again is it possible to utilise this with your code to include custom fields?
    Thanks for your help.

    1. You need to add ‘true’ as the third parameter. Ex: get_post_meta( $related->ID, ‘name of custom field’, true ); See the Codex for more information:

      I’ve never used the widget so I don’t know how customizable it is. The best option is to dig through the code looking for where ‘context’ => ‘widget’ creates the markup and see if it’s filterable.

  6. This is great, and I’ve almost got it figured out. I’m running into a problem on a site on which I’m using Edit Flow to create custom statuses. At first, I thought it wasn’t displaying related posts I’d selected, but then I realized they were saved as “Up for Grabs” or another status that means “not published,” basically. I am guessing I need to specify somewhere in here that the status needs to be “published,” but I’m not sure where that should go. Would appreciate it if you have a chance to chime in. Thanks!

      1. Okay, thanks anyway. I hacked away at it several times last night, cried, vowed never to set foot on the Internet again, and decided to take a break. If I do figure it out I’ll come back and post it here in case someone else is searching for similar information.

  7. Is there a trick to getting this working if you have your page code split into two php files, like, say,
    archive.php containing get_template_part(‘loop’).

    I have the be_related_posts_pre_loop() function in archive.php, and be_related_posts() in the loop.php.

    When I put all loop code in the archive.php, this works. When i use a separate loop.php, the connections seem to be empty, though the rest of each post (the_title(), the_[ermalink(), the_content(), etc.) all work fine.

  8. Hi Bill,

    This is a great article and a great tutorial on how to get things up and running. Hope you don’t mind me asking a couple of questions:

    1. Is there a way to check if there are any related articles, so I know whether to output a section header, for example?
    2. How do I limit the number of related posts to display?
    3. What would happen if you tried to use this outside the loop on an archive page? Would it display any related posts related to any articles on that archive page?

    Thanks in advance!

    1. 1. Yes. Line 14 of this code snippet featured above checks to see if there’s connected posts. After it I add some markup before displaying the actual posts.

      2. On line 18 of the above-linked snippt, change $count < 6 to however many you'd like displayed. 3. No, it will not work outside the loop of an archive page.

  9. Please make it easier for newbies to understand. First you mentioned plugins then the code, 🙁 Don’t know what to do 🙁
    Please share 2 lines with me so that I also come up with related posts.

    1. I’m sorry but this tutorial is not designed for newbies. I recommend you use one of the plugins mentioned at the top for related posts rather than building the custom solution described in detail here.

  10. How might you add a fall back to list other articles from the same category or tag?
    I would like to design my page so there are always 5 related posts, with the option to curate them. This was if an author is lazy and doesn’t make connections then there are still some listed similar posts?

    1. Once you’re done looping through the connections, check the $count variable. If it’s less than what you want, do a WordPress query for posts in the same category, with ‘posts_per_page’ => 5 – $count

  11. I found this excellent post after 2-1/2 years – thanks Bill! Since this was written, Genesis has switched to support for HTML5 and added some new hooks. If you’re using a more-recent Genesis child theme that’s designed for HTML5, use the genesis_before_entry and genesis_entry_content hooks.

    If your child theme is designed to work with older Genesis versions and XHTML, stick with the genesis_before_loop and genesis_after_post_content hooks.

    Thanks again, Bill E. Terrific stuff!

Leave a comment