WordPress Previous and Next Post Links With Thumbnails

WordPress previous and next post links usually show only the title. This snippet shows the previous and next post under a single post with its title and featured image, as one navigation block you can style.

It replaces the old template code that printed a broken link and PHP warnings on the first and the last post, because there is no previous or next post there. Now a missing side is simply left out, and every title and URL is escaped.

The snippet

<?php
/**
 * Previous and next post, each with its title and featured image.
 * Scripts Organizer: Trigger location Conditions, Script location Shortcode.
 */
$dpc_image_size  = 'thumbnail';            // Any registered image size.
$dpc_image_class = 'dpc-adjacent__thumb';  // Class on the <img>.
$dpc_same_term   = false;                  // true: only posts in the same category.

$dpc_adjacent = array(
	'prev' => array( get_previous_post( $dpc_same_term ), __( 'Previous post', 'dpc' ) ),
	'next' => array( get_next_post( $dpc_same_term ), __( 'Next post', 'dpc' ) ),
);

$dpc_links = '';
foreach ( $dpc_adjacent as $dpc_rel => $dpc_item ) {
	list( $dpc_adjacent_post, $dpc_label ) = $dpc_item;
	// null without a current post, '' on the first or last post.
	if ( ! $dpc_adjacent_post instanceof WP_Post ) {
		continue;
	}
	$dpc_links .= sprintf(
		'<a class="dpc-adjacent__link dpc-adjacent__link--%1$s" href="%2$s" rel="%1$s"><span class="dpc-adjacent__label">%3$s</span>%4$s<h3 class="dpc-adjacent__title">%5$s</h3></a>',
		esc_attr( $dpc_rel ),
		esc_url( get_permalink( $dpc_adjacent_post ) ),
		esc_html( $dpc_label ),
		get_the_post_thumbnail( $dpc_adjacent_post, $dpc_image_size, array( 'class' => $dpc_image_class ) ),
		esc_html( get_the_title( $dpc_adjacent_post ) )
	);
}

if ( '' !== $dpc_links ) {
	echo '<nav class="dpc-adjacent" aria-label="' . esc_attr__( 'Post navigation', 'dpc' ) . '">' . $dpc_links . '</nav>';
}

Add it with Scripts Organizer

In Scripts Organizer go to Code Blocks → Add New and give the block a name. Set Trigger location to Conditions, tick Shortcode under Script location (leave PHP unticked), paste the code into the PHP editor, keep Template on All and publish.

The block now shows its shortcode, for example [scorg_shortcode id="123"]. Paste it where the output should appear: a Shortcode block in the post or in a block theme template, or the shortcode element of your page builder’s single or archive template.

This is the Scripts Organizer way for template code like this: no shortcode to register, and the block’s Conditions decide where it prints. Set Template to Post Type → Posts if the same shortcode also sits in a template shared with pages. Scripts Organizer includes this file every time the shortcode renders, inside its own function. That is why the code has no function declarations (a second shortcode on the same page would fatal with “Cannot redeclare”), uses get_the_ID() or get_queried_object_id() instead of a bare $post, and prefixes its variables with dpc_.

How it works

  • get_previous_post() and get_next_post() look at the current post in the main query. They return a post, an empty string on the first or last post, and null when there is no current post, so the instanceof WP_Post check covers all three.
  • get_the_post_thumbnail() prints the image with srcset, sizes, lazy loading and the alt text from the media library. A post without a featured image just has no <img>.
  • Each side is a link with rel="prev" or rel="next", a small label, the image and an h3 title. The whole thing sits in a nav with an aria-label, and nothing is printed when both sides are missing.
  • Titles go through esc_html() and URLs through esc_url(), so a title with & or HTML cannot break the markup.

Customise it

  • Bigger images: set $dpc_image_size to medium or your theme’s size.
  • Only posts from the same category: set $dpc_same_term to true. The older same-category version of this post covers that case in detail.
  • Different heading level: change both h3 tags in the sprintf() pattern.
  • Side by side: .dpc-adjacent{display:flex;justify-content:space-between;gap:1rem} and .dpc-adjacent__link--next{text-align:right;margin-left:auto}.

Gotchas

  • Previous and next follow the publish date of the same post type. Sticky posts and custom ordering are ignored.
  • In a page builder’s preview or the Site Editor there may be no current post, so the block stays empty there and appears on the real post.
  • Block themes already have a Post Navigation Link block without images. Use this when you need the thumbnails.

In the Scripts Organizer library Import it with one click from Scripts Organizer.

// newsletter

Three tested WordPress snippets a week

Blocks, security, performance, Tailwind, PHP and React. Copy, paste, ship. You’ll also get DPlugins product updates. No spam, and you can unsubscribe anytime.

// newsletter

Three tested WordPress snippets a week

Blocks, security, performance, Tailwind, PHP and React. Copy, paste, ship. You'll also get DPlugins product updates. No spam, and you can unsubscribe anytime.