WordPressサイトで投稿記事の下部によく見かける「前の記事・次の記事」のナビゲーション。これがあることで、読者が関連する記事を簡単に見つけられ、サイトの回遊率向上にもつながります。
今回は、カスタムテーマでこの機能を実装する方法を、コードの技術的な解説も含めて詳しく説明します。
完成イメージ
記事の下部に、前の記事と次の記事が横並びで表示され、それぞれにサムネイル画像、記事タイトル、「前の記事」「次の記事」のラベルが配置されたナビゲーションを作成します。レスポンシブ対応で、スマートフォンでは縦並びになります。
PHPコードの実装
まず、前後の記事情報を取得・表示するPHPコードから見ていきましょう。
prevnextPost.php
<!-- ▼prevnextPost.php(前の記事・次の記事) -->
<?php
/**
* 記事ナビゲーションデータを取得する関数
*/
function get_post_navigation_data($post, $default_title = '記事がありません') {
if (!$post) {
return [
'title' => '<h6 class="prxtPost-title">' . $default_title,
'link' => '<a class="hasnt-post">',
'thumbnail' => ''
];
}
$title = '<h6 class="prxtPost-title">' . esc_html(strip_tags(get_the_title($post->ID)));
$link = '<a href="' . esc_url(get_permalink($post->ID)) . '">';
if (has_post_thumbnail($post->ID)) {
$thumbnail = get_the_post_thumbnail($post->ID, 'thumbnail');
} else {
$noimage_url = get_theme_file_uri('assets/images/default_image.png');
$thumbnail = '<img src="' . esc_url($noimage_url) . '" alt="デフォルト画像" />';
}
return compact('title', 'link', 'thumbnail');
}
// 前後の記事を取得
$prevPost = get_previous_post();
$nextPost = get_next_post();
// ナビゲーションデータを取得
$prev = get_post_navigation_data($prevPost);
$next = get_post_navigation_data($nextPost);
?>
<?php if ($prevPost || $nextPost) : ?>
<!-- 前の記事か次の記事のどちらかが存在しているなら -->
<div id="prevnextBvzN" class="prxtPost-container">
<div class="prxtPost-wrapper">
<!-- 前の記事 -->
<?php echo $prev['link']; ?>
<dl class="prxtPost-slide-prev">
<dt class="prxtPost-term"><?php echo $prev['thumbnail']; ?></dt>
<dd class="prxtPost-data">
<?php echo $prev['title'] . '</h6>'; ?>
<u class="prxtPost-upper"><i class="icon"></i>前の記事</u>
</dd>
</dl>
</a>
<!-- 次の記事 -->
<?php echo $next['link']; ?>
<dl class="prxtPost-slide-next">
<dt class="prxtPost-term"><?php echo $next['thumbnail']; ?></dt>
<dd class="prxtPost-data">
<?php echo $next['title'] . '</h6>'; ?>
<u class="prxtPost-upper"><i class="icon"></i>次の記事</u>
</dd>
</dl>
</a>
</div>
</div><!-- ///.prxtPost-container -->
<?php endif; ?>
<!-- ///▲prevnextPost.php -->
コードの技術的解説
1. 前後記事の取得
$prevPost = get_previous_post();
$nextPost = get_next_post();
WordPressの標準関数を使用して、現在の記事の前後の記事を取得しています。これらの関数は投稿日時順で前後の記事を判定します。
2. 関数化によるコードの整理
function get_post_navigation_data($post, $default_title = '記事がありません') {
// ...
}
前の記事と次の記事で同じ処理を行うため、共通処理を関数化しています。これにより:
- コードの重複を削減
- メンテナンスが容易
- 処理の統一性を保証
3. セキュリティ対策
$title = '<h6 class="prxtPost-title">' . esc_html(strip_tags(get_the_title($post->ID)));
$link = '<a href="' . esc_url(get_permalink($post->ID)) . '">';
重要なセキュリティ処理:
strip_tags(): HTMLタグを除去(記事タイトルに含まれる<br>等が表示されるのを防ぐ)esc_html(): HTMLエスケープ処理esc_url(): URL のサニタイズ
4. サムネイル画像の処理
if (has_post_thumbnail($post->ID)) {
$thumbnail = get_the_post_thumbnail($post->ID, 'thumbnail');
} else {
$noimage_url = get_theme_file_uri('assets/images/default_image.png');
$thumbnail = '<img src="' . esc_url($noimage_url) . '" alt="デフォルト画像" />';
}
ポイント:
has_post_thumbnail(): アイキャッチ画像の存在チェックget_theme_file_uri(): WordPress 4.7以降の推奨方式(子テーマ対応)- フォールバック画像の設定で、画像がない記事でもレイアウトが崩れない
5. 条件分岐による表示制御
<?php if ($prevPost || $nextPost) : ?>
前の記事または次の記事のどちらかが存在する場合のみナビゲーションを表示します。
CSSスタイリング
/* prevnextPost:CSS(前の記事・次の記事)
-------------------------------------- */
.prxtPost-container {
margin-top: 2pc;
display: block;
width: 728px;
max-width: 100%;
height: auto;
margin-inline: auto;
overflow: visible;
}
.prxtPost-wrapper {
width: 100%;
}
.prxtPost-wrapper a {
display: inline-block;
width: 100%;
height: 100%;
background: hsla(0, 0%, 98%, 1);
}
/* hover */
@media (any-hover: hover) {
.prxtPost-wrapper a[href]:hover {
background: hsla(0, 0%, 91%, 1);
filter: brightness(1.05);
}
}
/* リンク無効 */
.prxtPost-wrapper a.hasnt-post {
pointer-events: none;
cursor: none;
}
.prxtPost-wrapper [class*="prxtPost-slide"] {
position: relative;
display: table;
width: 100%;
height: 90px;
background: transparent;
border: 1px var(--c-silver, hsl(223, 6%, 75%)) solid;
}
.prxtPost-wrapper .prxtPost-term {
width: 90px;
height: 90px;
background: var(--c-whitesmoke, hsl(223, 6%, 96%));
}
.prxtPost-wrapper .prxtPost-term img {
width: 90px;
height: 90px;
object-fit: cover;
}
/* img + padding */
.prxtPost-wrapper .prxtPost-data {
display: table-cell;
vertical-align: middle;
width: 100%;
height: auto;
/* padding: 左 右 */
padding-inline: 1pc;
text-align: center;
}
.prxtPost-wrapper .prxtPost-title {
font-size: 15px;
font-weight: 500;
text-align: center;
color: var(--c-text, hsl(223, 6%, 13%));
}
.prxtPost-wrapper a.hasnt-post .prxtPost-title {
color: var(--c-silver, hsl(223, 6%, 75%));
}
.prxtPost-wrapper .prxtPost-upper {
/* padding: 上 横 下 */
padding: 5px 0px 5px;
font-size: 14px;
font-weight: 600;
color: var(--c-text);
}
.prxtPost-wrapper .prxtPost-upper {
position: absolute;
top: -38px;
left: 0px;
}
/* prxtPost:CSS(SP) */
@media screen and (max-width: 743.9px) {
.prxtPost-wrapper .prxtPost-slide-next {
margin-top: 40px;
}
.prxtPost-wrapper .prxtPost-upper .icon {
position: relative;
display: inline-grid;
place-content: center;
place-items: center;
margin-left: -6px;
}
.prxtPost-wrapper .prxtPost-upper .icon::before {
position: relative;
top: 6px;
display: inline-grid;
place-content: center;
place-items: center;
font-size: 173.3%;
/* Material Symbols */
font-family: 'Material Symbols Sharp';
font-variation-settings:
'FILL' 1,
'wght' 300;
color: var(--c-primary, hsl(223, 62%, 23%));
}
.prxtPost-wrapper .prxtPost-slide-prev .prxtPost-upper .icon::before {
content: "\\e045";
}
.prxtPost-wrapper .prxtPost-slide-next .prxtPost-upper .icon::before {
content: "\\e044";
}
}
/* (PC) */
@media print,
screen and (min-width: 744px) {
.prxtPost-wrapper {
display: flex;
justify-content: center;
align-items: center;
}
.prxtPost-wrapper [class*="prxtPost-slide"] {
height: 120px;
}
.prxtPost-wrapper .prxtPost-slide-next {
border-left: none;
}
.prxtPost-wrapper .prxtPost-term {
position: absolute;
margin: auto;
}
.prxtPost-wrapper .prxtPost-slide-prev .prxtPost-term {
bottom: 0px;
left: 0px;
}
.prxtPost-wrapper .prxtPost-slide-next .prxtPost-term {
bottom: 0px;
right: 0px;
}
/* img + padding */
.prxtPost-wrapper .prxtPost-slide-prev .prxtPost-data {
/* padding: 上 右 下 左 */
padding: 0px 15px 0px 105px;
}
.prxtPost-wrapper .prxtPost-slide-next .prxtPost-data {
/* padding: 上 右 下 左 */
padding: 0px 105px 0px 15px;
}
.prxtPost-wrapper .prxtPost-upper {
position: absolute;
margin: auto;
width: 91px;
/* padding: 上 横 下 */
padding: 5px 12px 5px;
background: var(--c-primary, hsl(223, 62%, 23%));
color: #FFF;
}
.prxtPost-wrapper .prxtPost-slide-prev .prxtPost-upper {
top: 0px;
left: -1px;
right: auto;
}
.prxtPost-wrapper .prxtPost-slide-next .prxtPost-upper {
top: 0px;
left: auto;
right: -1px;
}
}
テーマファイルへの組み込み
single.php への追加
<?php get_header(); ?>
<main>
<?php if (have_posts()) : ?>
<?php while (have_posts()) : the_post(); ?>
<article>
<h1><?php the_title(); ?></h1>
<div class="post-content">
<?php the_content(); ?>
</div>
</article>
<!-- 前の記事・次の記事 -->
<?php get_template_part('template-parts/prevnextPost'); ?>
<?php endwhile; ?>
<?php endif; ?>
</main>
<?php get_footer(); ?>
カスタマイズのポイント
1. カテゴリー内での前後記事取得
同じカテゴリー内の記事のみを対象にしたい場合:
$prevPost = get_previous_post(true); // 第1引数をtrueに
$nextPost = get_next_post(true);
2. 特定のカテゴリーを除外
$prevPost = get_previous_post(false, '1,5,9'); // カテゴリーID 1,5,9を除外
$nextPost = get_next_post(false, '1,5,9');
3. デフォルト画像のパス変更
$noimage_url = get_theme_file_uri('assets/images/your-default-image.png');
まとめ
この実装により、以下の特徴を持つ前後記事ナビゲーションが完成します:
- セキュアな実装: 適切なエスケープ処理
- レスポンシブ対応: PC・スマートフォン両方に最適化
- 保守性: 関数化による整理されたコード
- ユーザビリティ: 直感的で使いやすいデザイン
読者の回遊率向上に効果的なこの機能を、ぜひあなたのWordPressテーマに実装してみてください。

