WordPress single.phpの使い方とカスタマイズ|個別投稿ページのテンプレート作成


single.phpとは、WordPressで個別の投稿記事を表示するためのテンプレートファイルです。ブログ記事やニュース記事など、1つの投稿を単体で表示するページのレイアウトを制御します。

この記事では、single.phpの基本的な書き方からカスタマイズ方法まで、実践的なコード例を交えて解説します。前後の記事リンク、関連記事の表示、カスタム投稿タイプ別テンプレートの作成方法など、個別投稿ページを自由に設計するために必要な知識を網羅しています。

WordPressテーマのテンプレートファイル全体の構成については、WordPressテーマのテンプレートファイル一覧と役割で詳しく解説しています。


single.phpとは?テンプレート階層での役割

single.phpは、WordPressのテンプレート階層(Template Hierarchy)において、個別の投稿記事を表示する際に使用されるテンプレートファイルです。ユーザーがブログ記事のURLにアクセスしたとき、WordPressは以下の優先順位でテンプレートファイルを検索します。

優先順位テンプレートファイル説明
1(最優先)single-{post_type}.phpカスタム投稿タイプ別テンプレート(例: single-product.php)
2single.phpすべての投稿タイプ共通の個別投稿テンプレート
3singular.php投稿・固定ページ共通のテンプレート
4(最終)index.phpすべてのページの最終フォールバック

たとえば「product」というカスタム投稿タイプがある場合、WordPressはまずsingle-product.phpを探し、なければsingle.php、それもなければsingular.php、最終的にはindex.phpを使用します。

通常の投稿(post)の場合はsingle-post.phpが最優先ですが、ほとんどのテーマではsingle.phpを作成して対応しています。一覧ページのテンプレートであるarchive.phpが複数の投稿をループ表示するのに対し、single.phpは1つの投稿を詳細に表示する役割を持ちます。


single.phpの基本的な書き方

single.phpの基本構造は、WordPressループの中で投稿データを取得・表示するというシンプルなものです。以下に、最小限の構成から実用的な構成まで順を追って解説します。

最小構成のsingle.php

まずは最もシンプルなsingle.phpの例です。ヘッダー・フッターの読み込みと、WordPressループ内でのタイトル・本文の表示が基本になります。

<?php get_header(); ?>

<main>
  <?php while ( have_posts() ) : the_post(); ?>

    <article>
      <h1><?php the_title(); ?></h1>
      <div class="post-content">
        <?php the_content(); ?>
      </div>
    </article>

  <?php endwhile; ?>
</main>

<?php get_footer(); ?>

have_posts()the_post()はWordPressループの基本です。single.phpでは投稿が1件だけですが、ループ構文は必須です。the_post()を呼ぶことでグローバルな投稿データがセットアップされ、the_title()the_content()などのテンプレートタグが正しく動作するようになります。

実用的な構成のsingle.php

実際のテーマでは、タイトルと本文に加えて、アイキャッチ画像、投稿日、著者名、カテゴリーなどのメタ情報も表示します。以下は実用的なsingle.phpの例です。

<?php get_header(); ?>

<main class="site-main">
  <?php while ( have_posts() ) : the_post(); ?>

    <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>

      <!-- 記事ヘッダー -->
      <header class="post-header">
        <h1 class="post-title"><?php the_title(); ?></h1>

        <div class="post-meta">
          <time datetime="<?php echo esc_attr( get_the_date( 'c' ) ); ?>">
            <?php echo esc_html( get_the_date() ); ?>
          </time>
          <span class="post-author">
            <?php the_author(); ?>
          </span>
          <span class="post-category">
            <?php the_category( ', ' ); ?>
          </span>
        </div>
      </header>

      <!-- アイキャッチ画像 -->
      <?php if ( has_post_thumbnail() ) : ?>
        <div class="post-thumbnail">
          <?php the_post_thumbnail( 'large' ); ?>
        </div>
      <?php endif; ?>

      <!-- 記事本文 -->
      <div class="post-content">
        <?php the_content(); ?>
      </div>

      <!-- タグ表示 -->
      <?php if ( has_tag() ) : ?>
        <div class="post-tags">
          <?php the_tags( '<span>', '</span><span>', '</span>' ); ?>
        </div>
      <?php endif; ?>

    </article>

  <?php endwhile; ?>
</main>

<?php get_sidebar(); ?>
<?php get_footer(); ?>

各テンプレートタグの役割は次のとおりです。

テンプレートタグ出力内容補足
the_title()投稿タイトルh1タグ内で使用する
the_content()投稿本文ブロックエディタの内容がそのまま出力される
the_post_thumbnail()アイキャッチ画像サイズ指定可能(thumbnail, medium, large, full)
get_the_date()公開日引数でフォーマット指定可能(例: ‘Y年n月j日’)
the_author()著者名リンク付きはthe_author_posts_link()を使用
the_category()カテゴリー名引数でセパレータを指定可能
the_tags()タグ名3つの引数で前後の文字列を指定可能
post_class()投稿のCSSクラス投稿タイプやカテゴリーに応じたクラスが自動付与される

WordPress関数の詳細については、WordPress関数一覧も参考にしてください。


前後の記事リンクを表示する

個別投稿ページでは、前後の記事へのナビゲーションリンクを設置するのが一般的です。WordPressにはprevious_post_link()next_post_link()という専用のテンプレートタグが用意されています。

基本的な前後リンクの実装

<nav class="post-navigation">
  <div class="nav-previous">
    <?php previous_post_link( '%link', '« %title' ); ?>
  </div>
  <div class="nav-next">
    <?php next_post_link( '%link', '%title »' ); ?>
  </div>
</nav>

%linkはリンクタグに置き換わり、%titleは前後の記事のタイトルに置き換わります。最初の記事には「前の記事」リンクが、最後の記事には「次の記事」リンクが自動的に非表示になります。

同一カテゴリー内での前後リンク

第3引数にtrueを渡すことで、同じカテゴリーの記事だけを対象に前後リンクを生成できます。カテゴリーごとにコンテンツの連続性を保ちたい場合に有効です。

<nav class="post-navigation">
  <div class="nav-previous">
    <?php previous_post_link( '%link', '« %title', true ); ?>
  </div>
  <div class="nav-next">
    <?php next_post_link( '%link', '%title »', true ); ?>
  </div>
</nav>

また、WordPress 4.1以降ではthe_post_navigation()関数も使えます。こちらはラッパーとなるHTMLも含めて出力してくれるため、シンプルに実装できます。

<?php
the_post_navigation( array(
    'prev_text' => '« %title',
    'next_text' => '%title »',
    'in_same_term' => true,
    'taxonomy'     => 'category',
) );
?>

関連記事を表示する方法

関連記事を表示することで、読者のサイト内回遊率を向上させることができます。プラグインを使わずに、同じカテゴリーの記事をWP_Queryで取得して表示する方法を紹介します。

同カテゴリーから関連記事を取得する

以下のコードでは、現在表示中の記事と同じカテゴリーから最新3件の関連記事を取得し、タイトルとアイキャッチ画像をリンク付きで表示します。

<?php
// 現在の投稿のカテゴリーIDを取得
$categories = wp_get_post_categories( get_the_ID() );

if ( ! empty( $categories ) ) :
    $related_query = new WP_Query( array(
        'post_type'           => 'post',
        'post_status'         => 'publish',
        'posts_per_page'      => 3,
        'post__not_in'        => array( get_the_ID() ),
        'category__in'        => $categories,
        'orderby'             => 'date',
        'order'               => 'DESC',
        'ignore_sticky_posts' => true,
        'no_found_rows'       => true,
    ) );

    if ( $related_query->have_posts() ) : ?>
        <section class="related-posts">
            <h2>関連記事</h2>
            <div class="related-posts-grid">
                <?php while ( $related_query->have_posts() ) : $related_query->the_post(); ?>
                    <a href="<?php the_permalink(); ?>" class="related-post-card">
                        <?php if ( has_post_thumbnail() ) : ?>
                            <?php the_post_thumbnail( 'medium' ); ?>
                        <?php endif; ?>
                        <h3><?php the_title(); ?></h3>
                    </a>
                <?php endwhile; ?>
            </div>
        </section>
    <?php endif;

    wp_reset_postdata();
endif;
?>

このコードのポイントは以下の3つです。

  1. post__not_in: 現在の記事自身を除外する
  2. no_found_rows: ページネーションが不要な場合はtrueにすることでクエリを高速化できる
  3. wp_reset_postdata(): サブクエリの後は必ず呼び出し、グローバルな投稿データを元に戻す

タグベースで関連記事を取得する

カテゴリーの代わりにタグを基準にすることもできます。category__intag__inに変更するだけです。

<?php
$tags = wp_get_post_tags( get_the_ID(), array( 'fields' => 'ids' ) );

if ( ! empty( $tags ) ) :
    $related_query = new WP_Query( array(
        'post_type'           => 'post',
        'post_status'         => 'publish',
        'posts_per_page'      => 3,
        'post__not_in'        => array( get_the_ID() ),
        'tag__in'             => $tags,
        'orderby'             => 'rand',
        'ignore_sticky_posts' => true,
        'no_found_rows'       => true,
    ) );

    // ... 表示処理はカテゴリーベースと同様 ...

    wp_reset_postdata();
endif;
?>

single-{post_type}.phpでカスタム投稿タイプ別にテンプレートを分ける

WordPressでは、カスタム投稿タイプごとに専用のsingle.phpを作成できます。テンプレートファイル名をsingle-{post_type}.phpの形式にするだけで、WordPressが自動的に認識します。

カスタム投稿タイプの登録

まず、functions.phpでカスタム投稿タイプを登録します。ここでは「portfolio」(制作実績)を例にします。

function my_register_portfolio_post_type() {
    register_post_type( 'portfolio', array(
        'labels' => array(
            'name'          => '制作実績',
            'singular_name' => '制作実績',
        ),
        'public'       => true,
        'has_archive'  => true,
        'supports'     => array( 'title', 'editor', 'thumbnail', 'custom-fields' ),
        'menu_icon'    => 'dashicons-portfolio',
        'rewrite'      => array( 'slug' => 'portfolio' ),
    ) );
}
add_action( 'init', 'my_register_portfolio_post_type' );

single-portfolio.phpの作成

テーマフォルダにsingle-portfolio.phpを作成すると、portfolioの個別投稿ページではこのテンプレートが自動的に使われます。通常の投稿とは異なるレイアウトを適用できます。

<?php
/**
 * 制作実績のカスタム投稿タイプ用テンプレート
 * テンプレートファイル名: single-portfolio.php
 */
get_header(); ?>

<main class="portfolio-detail">
  <?php while ( have_posts() ) : the_post(); ?>

    <article class="portfolio-item">
      <h1><?php the_title(); ?></h1>

      <?php if ( has_post_thumbnail() ) : ?>
        <div class="portfolio-hero">
          <?php the_post_thumbnail( 'full' ); ?>
        </div>
      <?php endif; ?>

      <!-- カスタムフィールドで追加情報を表示 -->
      <div class="portfolio-meta">
        <?php
        $client = get_post_meta( get_the_ID(), 'client_name', true );
        $url    = get_post_meta( get_the_ID(), 'project_url', true );
        ?>
        <?php if ( $client ) : ?>
          <p><strong>クライアント:</strong> <?php echo esc_html( $client ); ?></p>
        <?php endif; ?>
        <?php if ( $url ) : ?>
          <p><strong>URL:</strong> <a href="<?php echo esc_url( $url ); ?>"><?php echo esc_html( $url ); ?></a></p>
        <?php endif; ?>
      </div>

      <div class="portfolio-content">
        <?php the_content(); ?>
      </div>
    </article>

  <?php endwhile; ?>
</main>

<?php get_footer(); ?>

カスタムフィールドの扱い方についてさらに詳しく知りたい場合は、WordPressカスタムフィールド解説をご覧ください。

よく使われるカスタム投稿タイプ別テンプレートの対応表をまとめます。

投稿タイプ(slug)テンプレートファイル名用途例
postsingle-post.php(または single.php)通常のブログ記事
portfoliosingle-portfolio.php制作実績
productsingle-product.php商品ページ(WooCommerce等)
eventsingle-event.phpイベント情報
faqsingle-faq.phpよくある質問の個別表示

functions.phpとの連携

single.phpで使用する機能の中には、functions.phpでの事前設定が必要なものがあります。ここでは、single.phpと密接に関連するfunctions.phpの設定を紹介します。

アイキャッチ画像のサポートを有効化する

single.phpでthe_post_thumbnail()を使用するには、functions.phpでアイキャッチ画像のサポートを有効化する必要があります。

function my_theme_setup() {
    // アイキャッチ画像のサポートを有効化
    add_theme_support( 'post-thumbnails' );

    // カスタム画像サイズの追加(任意)
    add_image_size( 'single-hero', 1200, 630, true );
}
add_action( 'after_setup_theme', 'my_theme_setup' );

カスタム画像サイズを追加した場合、single.phpではthe_post_thumbnail( 'single-hero' )のように指定して使用できます。

抜粋文(excerpt)のカスタマイズ

single.php自体では抜粋文を使う場面は少ないですが、関連記事の表示で抜粋文を使いたい場合は、文字数やデフォルトの「続きを読む」テキストをカスタマイズできます。

// 抜粋文の文字数を変更(デフォルト: 55語)
function my_excerpt_length( $length ) {
    return 40;
}
add_filter( 'excerpt_length', 'my_excerpt_length' );

// 抜粋文の「続きを読む」テキストを変更
function my_excerpt_more( $more ) {
    return '...';
}
add_filter( 'excerpt_more', 'my_excerpt_more' );

投稿ページ専用のCSSやJSを読み込む

single.phpでのみ必要なCSSやJavaScriptファイルがある場合、is_single()で条件分岐して読み込むことで、他のページへの影響を防ぎつつパフォーマンスを最適化できます。

function my_single_scripts() {
    if ( is_single() ) {
        wp_enqueue_style(
            'single-style',
            get_template_directory_uri() . '/assets/css/single.css',
            array(),
            '1.0.0'
        );
        wp_enqueue_script(
            'single-script',
            get_template_directory_uri() . '/assets/js/single.js',
            array(),
            '1.0.0',
            true
        );
    }
}
add_action( 'wp_enqueue_scripts', 'my_single_scripts' );

functions.phpの便利なカスタマイズについては、functions.php便利コード集でさらに多くの例を紹介しています。


よくある質問(FAQ)

Q. single.phpとpage.phpの違いは何ですか?

single.phpは「投稿(post)」やカスタム投稿タイプの個別ページに使用されるテンプレートです。一方、page.phpは「固定ページ」に使用されます。投稿にはカテゴリーやタグがあり時系列で表示されるのに対し、固定ページは「会社概要」「お問い合わせ」のような独立したコンテンツに使います。

Q. single.phpがないテーマではどうなりますか?

テーマにsingle.phpが存在しない場合、WordPressはテンプレート階層に従ってsingular.phpを探し、それもなければindex.phpを使用して投稿を表示します。ただし、index.phpは汎用テンプレートであるため、投稿に適したレイアウトにならない可能性が高いです。投稿を表示するテーマではsingle.phpの作成を推奨します。

Q. single.phpでサイドバーを表示するにはどうすればよいですか?

single.phpの中でget_sidebar()を呼び出すことでサイドバーを表示できます。通常はメインコンテンツの後、get_footer()の前に配置します。CSSでメインコンテンツとサイドバーを横並びにレイアウトするために、親要素にFlexboxやCSS Gridを適用するのが一般的です。

Q. wp_reset_postdata()を呼び忘れるとどうなりますか?

関連記事の表示などでサブクエリ(WP_Query)を使った後にwp_reset_postdata()を呼ばないと、グローバルな$post変数がサブクエリの最後の投稿のままになります。その結果、それ以降のテンプレートタグ(the_title()やthe_permalink()など)が本来の投稿ではなくサブクエリの投稿のデータを返してしまい、意図しない表示になります。

Q. 特定のカテゴリーの投稿だけレイアウトを変えることはできますか?

はい、可能です。single.php内でin_category()を使って条件分岐する方法と、get_template_part()でカテゴリー別のテンプレートパーツを読み込む方法があります。たとえばif ( in_category( 'news' ) ) { get_template_part( 'template-parts/single', 'news' ); }のように記述すると、「news」カテゴリーの投稿だけ別のテンプレートパーツで表示できます。

Q. single.phpの中でget_template_part()を使うメリットは何ですか?

get_template_part()を使うことで、single.phpのコードを複数の小さなファイルに分割できます。たとえば記事ヘッダー、記事本文、関連記事、著者情報などをそれぞれ別ファイルにすることで、コードの見通しがよくなり保守性が向上します。また、分割したパーツを他のテンプレート(page.phpなど)でも再利用できるメリットもあります。


まとめ

single.phpは、WordPressの個別投稿ページを制御する中核のテンプレートファイルです。この記事で解説した内容をまとめます。

  • テンプレート階層: single-{post_type}.php → single.php → singular.php → index.phpの順に検索される
  • 基本構造: WordPressループの中でthe_title()、the_content()、the_post_thumbnail()などのテンプレートタグで投稿データを表示する
  • 前後リンク: previous_post_link() / next_post_link()で記事間のナビゲーションを実装する
  • 関連記事: WP_Queryで同カテゴリー・同タグの記事を取得し、サイト内回遊率を向上させる
  • カスタム投稿タイプ: single-{post_type}.phpで投稿タイプごとに専用レイアウトを作成できる
  • functions.phpとの連携: アイキャッチ画像のサポート有効化、抜粋文カスタマイズ、条件付きCSS/JS読み込み

single.phpを理解し使いこなすことで、投稿ページのデザインと機能を自由にカスタマイズできるようになります。テンプレートファイルの全体像を把握するために、WordPressテーマのテンプレートファイル一覧と役割もあわせてご覧ください。

【30,000円OFFクーポン】【国内生産・公式… 【30,000円OFFクーポン】【国内生産・公式… ¥139,800 【P20倍】【国内生産・公式】 新品 大画面… 【P20倍】【国内生産・公式】 新品 大画面… ¥179,799 【楽天1位常連・超700冠獲得】黒/白 モニ… 【楽天1位常連・超700冠獲得】黒/白 モニ… ¥11,999 【ふるさと納税】液晶モニター31.5型ワイ… 【ふるさと納税】液晶モニター31.5型ワイ… ¥135,000 ロジクール ワイヤレスキーボード K295GP … ロジクール ワイヤレスキーボード K295GP … ¥3,201 【ふるさと納税】HHKB Professional HYBRI… 【ふるさと納税】HHKB Professional HYBRI… ¥130,000
Rakuten Web Service Center