【WordPress】カスタム投稿とタクソノミーで作る「よくある質問(FAQ)」実装方法
WordPressでサイトを制作する際、固定ページに直接FAQ(よくある質問)をベタ書きすると、運用フェーズで「管理しづらい」「他のページで使い回せない」という問題が発生しがちです。
プラグインで対応する前にまずは自分で構造化データまで確実に出力できるものを作ろうと思い、自サイトで試作しました。
今回は、管理画面から誰でも簡単に質問と回答を追加でき、カテゴリごとの出し分けやテンプレートパーツ化にも対応したカスタム投稿によるFAQの実装方法を、備忘録を兼ねて解説します。
FAQを作る代表的な方法
作り方を紹介する前に、FAQを作る方法として代表的な方法を紹介しておきます。
WordPressでFAQを作成する方法はいくつかありますが、それぞれメリット・デメリットがあるため、サイトの規模や運用方法に合わせて選ぶのがおすすめです。
1. 手動HTMLで作成
メリット
- 自由にデザインできる
- プラグイン不要で動作が軽い
- シンプルなFAQならすぐ作れる
デメリット
- 質問を追加・修正するたびにコードを編集する必要がある
- FAQが増えると管理しにくい
- 他のページへの使い回しは大変
2. プラグインで作成
例:Ultimate FAQ、Easy Accordion、JetMonstar FAQ、Heroic FAQsなど
メリット
- 専門知識がなくても導入しやすい
- 並び替えや検索機能など便利な機能が揃っている
- 短時間で実装できる
デメリット
- 不要な機能まで読み込まれることがある
- デザインの自由度に制限がある場合も
- プラグインの更新や将来的な互換性に依存する
3. ブロックエディタ(Gutenberg)で作成
テーマ依存のQ&Aパーツなら簡単に追加できて便利です。
メリット
- コードを書かずに作成できる
- パソコンの操作に慣れていない人でも使いやすい
- 小規模なFAQに向いている
デメリット
- FAQが多くなると管理しづらい
- 同じFAQを複数ページで使い回しにくい
- カテゴリーごとの表示切り替えなどには向かない
カスタム投稿で作成(今回の方法)
メリット
- 管理画面から簡単に追加・編集できる
- カテゴリーごとに表示を切り替えられる
- テンプレートパーツとして使い回せる
- FAQが増えても管理しやすい
- 独自機能(並び替え・JSON-LD自動生成など)も実装しやすい
デメリット
- 初期構築に手間がかかる
- FAQが多くなると管理しづらい
- WordPressのカスタマイズ知識が必要
シンプルなQ&AならYoast SEOのFAQブロックもおすすめ
WordPressのSEOプラグインとして有名な『Yoast SEO』には、標準で「Yoast FAQブロック」という機能がついています。これを使えば、プラグインなしで構造化データ付きのFAQをページ内に作ることができます。
Q&Aを1ページのみで使っていて、SEO対策でYoast SEOを使用している場合は、Yoast FAQブロックで問題ありません。
それぞれのメリット・デメリットを比較してみます。
| 比較項目 | Yoast FAQブロック | 今回のカスタム投稿+パーツ化 |
|---|---|---|
| データの管理方法 | 固定ページごとのベタ書き | 専用メニューでの一括管理 |
| 複数ページでの使い回し | できない(ページごとに作る必要あり) | できる(1箇所直せば全ページ連動) |
| プランやカテゴリごとの出し分け | 手動で選んで配置 | 自動化できる(タクソノミーで制御) |
| デザインの自由度 | プラグインの仕様に縛られやすい | 完全自由(自作のCSS/JSがそのまま活きる) |
どちらを選ぶべきか迷った場合
Yoast FAQブロックが向いているケース
「会社概要ページに、そのページ専用のFAQを3つだけ載せたい」など、他のページで使い回す予定がない場合は、プラグインの手軽さを活かせるのでおすすめです。
今回のカスタム投稿ルートが向いているケース(コーポレート・サービスサイト向け)
「料金に関するFAQを、トップページとサービス詳細ページとLPの3箇所に載せたい」「プランごとに表示するFAQを自動で切り替えたい」という場合は、今回のカスタム投稿+パーツ化の構成がおすすめです。
今回作成するFAQの仕様
今回実装するFAQシステムは、以下のような要件を満たすように設計します。
- 管理画面の専用メニューから「タイトル=質問(Q)」「本文=回答(A)」として登録する。
- カスタムタクソノミー(カテゴリ)を紐付け、親カテゴリ(例:ホームページ制作)や子カテゴリ(例:Starterプラン)ごとに表示を切り替えられる。
- 表示順を番号(menu_order)で管理し、管理画面から任意の順番に並び替えられる。
- テンプレートパーツとしてFAQセクションを共通化し、任意のページ(サービス詳細ページなど)から1行で呼び出せる。
- 検索結果にFAQがリッチリザルトとして表示されるよう、構造化データを自動出力する。
【STEP1】カスタム投稿タイプとタクソノミーの登録
まずは functions.php に以下のコードを追加し、カスタム投稿タイプ faq と、専用のタクソノミー(カテゴリ) faq_category を登録します。
管理画面から各FAQに「表示順(番号)」を設定できるよう、supports パラメータに ‘page-attributes’(ページ属性) を指定しているのがポイントです。
function register_faq_custom_post() {
// 1. カスタム投稿タイプ「FAQ」の登録
register_post_type('faq', [
'labels' => [
'name' => 'FAQ(よくある質問)',
'singular_name' => 'FAQ',
'menu_name' => 'FAQ',
'add_new_item' => '新しいFAQを追加',
'edit_item' => 'FAQを編集',
],
'public' => true,
'has_archive' => false, // アーカイブページ(一覧)が不要な場合はfalse
'menu_icon' => 'dashicons-editor-help', // 管理画面のアイコン
// 【重要】'page-attributes' を追加することで、管理画面で「順序」が入力可能になります
'supports' => ['title', 'editor', 'page-attributes'],
'show_in_rest' => true, // ブロックエディタ(Gutenberg)を有効化
]);
// 2. カスタムタクソノミー「FAQカテゴリ」の登録
register_taxonomy('faq_category', 'faq', [
'labels' => [
'name' => 'FAQカテゴリ',
'singular_name' => 'FAQカテゴリ',
'search_items' => 'FAQカテゴリを検索',
'all_items' => 'すべてのFAQカテゴリ',
'parent_item' => '親カテゴリ',
'edit_item' => 'FAQカテゴリを編集',
'update_item' => 'FAQカテゴリを更新',
'add_new_item' => '新規FAQカテゴリを追加',
],
'hierarchical' => true, // 階層構造を持たせる(投稿のカテゴリと同じ仕様)
'show_in_rest' => true,
]);
}
add_action('init', 'register_faq_custom_post');コードを保存したら、WordPress管理画面の「設定」>「パーマリンク」に移動し、何も変更せずに「変更を保存」ボタンを押してください(これをしないと、カスタム投稿に関連するURLが404エラーになることがあります) 。
【STEP2】テンプレートパーツの作成
テーマの template-parts ディレクトリ内に faq.php を作成します。 このパーツを呼び出す際、引数($args)でスラッグを指定することで、特定のカテゴリに絞り込んだFAQを出力できるようにします。
同時に、SEO対策(検索エンジンにFAQであることを正しく伝えるため)として必須の構造化データ(JSON-LD)も自動生成する仕組みを組み込みます。
template-parts/faq.php 内に以下を記入
<?php
/**
* FAQ表示用テンプレートパーツ
*
* 呼び出し例:
* get_template_part('template-parts/faq', null, ['category' => 'starter-plan']);
*/
// 呼び出し元から渡されたカテゴリスラッグを取得
// 指定がない場合は全FAQを表示
$cat_slug = $args['category'] ?? '';
// FAQを取得するためのクエリ設定
$query_args = [
'post_type' => 'faq', // FAQのカスタム投稿
'posts_per_page' => -1, // 全件取得
'orderby' => 'menu_order', // 管理画面で設定した表示順
'order' => 'ASC', // 昇順(小さい番号から表示)
];
// カテゴリが指定されている場合は、そのカテゴリのみ取得
if (!empty($cat_slug)) {
$query_args['tax_query'] = [
[
'taxonomy' => 'faq_category',
'field' => 'slug',
'terms' => $cat_slug,
]
];
}
// FAQを取得
$faq_query = new WP_Query($query_args);
// JSON-LD(構造化データ)を格納する配列
// ループ内で質問・回答を追加していく
$faq_schema = [
'@context' => 'https://schema.org',
'@type' => 'FAQPage',
'mainEntity' => [],
];
if ($faq_query->have_posts()) : ?>
<div class="faq-container">
<dl class="faq-list">
<?php while ($faq_query->have_posts()) : $faq_query->the_post();
// タイトルを質問、本文を回答として取得
$question = get_the_title();
$answer = get_the_content();
// JSON-LD用のデータを追加
// HTMLタグは不要なのでプレーンテキストに変換
$faq_schema['mainEntity'][] = [
'@type' => 'Question',
'name' => $question,
'acceptedAnswer' => [
'@type' => 'Answer',
'text' => wp_strip_all_tags($answer),
],
];
?>
<div class="faq-item">
<!-- 質問 -->
<dt class="faq-question">
<span class="faq-icon-q">Q</span>
<?php echo esc_html($question); ?>
<span class="faq-toggle-icon"></span>
</dt>
<!-- 回答 -->
<dd class="faq-answer">
<div class="faq-answer-inner">
<span class="faq-icon-a">A</span>
<div class="faq-answer-content">
<?php
// 本文はHTMLとして表示
// wpautop()で改行を<p>タグへ変換
echo wp_kses_post(wpautop($answer));
?>
</div>
</div>
</dd>
</div>
<?php endwhile; ?>
</dl>
</div>
<!--
FAQPageの構造化データ(JSON-LD)を出力
FAQを追加・編集すると、このJSONも自動で更新される
-->
<script type="application/ld+json">
<?php
echo wp_json_encode(
$faq_schema,
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT
);
?>
</script>
<?php
endif;
// グローバル$postを元に戻す
wp_reset_postdata();
?>以下コードの解説なので必要のない方は読み飛ばしてもらって大丈夫です。
構造化データ(JSON-LD)の実装
FAQを表示するだけでなく、検索エンジンにも「このページはFAQである」と正しく伝えるために、構造化データ(JSON-LD)も出力します。
今回は、FAQを取得するループの中でHTMLの表示とJSON-LDの生成を同時に行っています。
同じループで生成する理由
FAQを表示するためにWP_Queryで投稿を取得しているので、そのデータをそのまま利用してJSON-LDも生成します。
もしHTML表示用とJSON-LD生成用で別々にWP_Queryを実行すると、同じデータを二度取得することになり、処理が無駄になってしまいます。
そのため、1回のループで
- 画面にFAQを表示する
- 構造化データ用の配列を作成する
という2つの処理を同時に行っています。
HTML表示用とJSON-LD用でデータを分ける
回答本文は用途によって扱い方が異なります。
画面に表示する回答は、改行や段落を反映させるためにwpautop()を使用してHTMLとして出力します。
一方、JSON-LDはプレーンテキストで出力する必要があるため、wp_strip_all_tags()でHTMLタグを除去しています。
このように表示用と構造化データ用で処理を分けることで、見た目を保ちながら正しいJSON-LDを生成できます。
wp_json_encode()を使用する
JSON-LDを出力する際はPHP標準のjson_encode()ではなく、WordPressが用意しているwp_json_encode()を使用します。
wp_json_encode()はWordPress環境に合わせたJSONの生成を行うため、文字コードなどの問題が起こりにくく、WordPress公式の開発者向けドキュメントでも推奨されている関数です。
参考ページはこちら
Googleのリッチリザルトについて
現在は一般的な企業サイトでFAQリッチリザルトが表示されるケースは以前より少なくなっています。
しかし、構造化データを実装することで検索エンジンがページ内容を理解しやすくなるため、SEOの基本施策として実装しておくことをおすすめします。
【STEP3】各ページで呼びす
作成したテンプレートパーツは、固定ページ(例:page-starter.php など)の表示したい箇所で以下のように呼び出します。
全てのFAQを一括表示したい場合
引数を渡さずに呼び出せば、登録されているすべてのFAQが出力されます。
<?php get_template_part('template-parts/faq'); ?>「Starterプラン」のFAQを表示したい場合
‘category’ => ‘starter-plan’を入力してください。
starter-planなどのカテゴリ名はご自分で登録した名前を使用してください。
<?php
get_template_part('template-parts/faq', null, [
'category' => 'starter-plan' // タクソノミーのタームスラッグを指定
]);
?>【STEP4】CSSとJavaScriptでアコーディオンを実装
見た目を整え、JavaScriptでスムーズに開閉するアニメーション(アコーディオン機能)を実装します。
アクセシビリティと滑らかな挙動を担保するため、dd(回答部分)は height: 0; overflow: hidden; で隠し、JSで高さを制御します。
.faq-container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.faq-item {
border-bottom: 1px solid #e0e0e0;
margin-bottom: 15px;
}
.faq-question {
display: flex;
align-items: center;
font-weight: bold;
padding: 15px 0;
cursor: pointer;
position: relative;
}
.faq-icon-q {
color: #ff5722;
font-size: 1.2rem;
margin-right: 10px;
}
.faq-toggle-icon {
margin-left: auto;
width: 12px;
height: 12px;
border-right: 2px solid #333;
border-bottom: 2px solid #333;
transform: rotate(45deg);
transition: transform 0.3s ease;
}
/* 開いた時の矢印の動き */
.faq-question.is-active .faq-toggle-icon {
transform: rotate(-135deg);
}
.faq-answer {
height: 0;
overflow: hidden;
transition: height 0.3s ease-in-out;
margin: 0;
}
.faq-answer-inner {
display: flex;
padding-bottom: 20px;
color: #555;
line-height: 1.6;
}
.faq-icon-a {
color: #009688;
font-weight: bold;
font-size: 1.2rem;
margin-right: 10px;
}動的アニメーション追加(JavaScript)
window.getComputedStyle や scrollHeight を活用し、CSSの height 変化だけでは難しい「コンテンツに応じた可変高さのスムーズなアニメーション」を実現します。
document.addEventListener('DOMContentLoaded', () => {
const faqQuestions = document.querySelectorAll('.faq-question');
faqQuestions.forEach(question => {
question.addEventListener('click', () => {
const answer = question.nextElementSibling;
// アクティブクラスの切り替え
question.classList.toggle('is-active');
if (question.classList.contains('is-active')) {
// 開く処理(中身の高さに合わせて動的に指定)
answer.style.height = answer.scrollHeight + 'px';
} else {
// 閉じる処理
answer.style.height = '0px';
}
});
});
});管理画面での実際の並び替え手順
‘page-attributes’ を有効にすると、各FAQの編集画面や一覧画面から並び順(menu_order)を変更できるようになります。WordPressの標準機能だけで順番をコントロールする具体的な手順は以下の通りです。
方法1:各FAQの編集画面から設定する
順番を変えたいFAQの編集画面を開きます。

画面右側のサイドバー(設定パネル)にある三点リーダの場所にマウスカーソルをあてます。

「順序」をクリックすると以下の表示になります。

入力欄に表示したい順に数値を入力します。
デフォルトは 0。一番上に表示したいFAQには1、二番目には2を入力。
「保存」を押して順序の表示を閉じ、ページの「保存」を押して更新します。
方法2:一覧画面から「クイック編集」で変更する
FAQの一覧画面から変更することも可能です。
まずはFAQの一覧画面を開き、順番を変えたいFAQのタイトルにマウスを合わせ、表示されるメニューから「クイック編集」をクリックします。

次に、右側に表示される「順序」の欄の数値を書き換え、左下の「更新」ボタンをクリックします。
デフォルトは 0なので、一番上に表示したいFAQには1、二番目には2を入力してください。

方法3:「Intuitive Custom Post Order」を導入する
標準機能でも問題なく並び替えは可能ですが、FAQの数が数十個に増えてくると「どれに何番を割り振ったか」を管理するのが大変になります。
そのため、もし直感的にドラッグ&ドロップで並び替えたい!という場合は、無料プラグイン の「Intuitive Custom Post Order」を導入するのがおすすめです。
プラグインの導入と設定手順
- プラグインのインストール
WordPress管理画面の「プラグイン」>「新規追加」から「Intuitive Custom Post Order」を検索し、インストールして有効化します。 - FAQの並び替えを有効にする
管理画面の「設定」>「並び替え設定」に移動します。 - 対象のカスタム投稿にチェックを入れる
「カスタム投稿タイプ」の項目にある 「FAQ(faq)」 にチェックを入れます。必要に応じて、カテゴリの順番もドラッグ&ドロップしたい場合は「カスタムタクソノミー」の 「FAQカテゴリ(faq_category)」 にもチェックを入れ、更新」をクリックします。
これで設定は完了です!
「FAQ」の一覧画面に戻ると、投稿をマウスで掴んで上下に入れ替えられるようになっています。
ちなみに、プラグインを導入しても、STEP 1で書いた functions.php や、STEP 2の faq.php のコードを変更する必要はありません。
プラグインが裏側で自動的に「順序(menu_order)」の数値を更新してくれるため、手動で数字を入力する手間が省けます。
【STEP5】リッチリザルトテストで構造化データを確認する
構造化データを実装したら、Googleの「リッチリザルトテスト」で正しく認識されているか確認しましょう。
確認方法はとても簡単です。
- Googleのリッチリザルトテストを開く
- 確認したいページのURLを入力する
- 「URLをテスト」をクリックする
- 解析が完了するまで待つ
解析が終わると、Googleが検出した構造化データの種類や、エラー・警告の有無が表示されます。
正常に認識された場合
FAQの構造化データが正しく実装されていると、「FAQ」が検出されたことが表示されます。
また、
- エラー:0件
- 警告:0件
となっていれば、構造化データとして問題なく認識されています。
エラーが表示された場合
構造化データに必須項目が不足していたり、JSONの記述ミスがあるとエラーが表示されます。
例えば、
- acceptedAnswer がない
- text が空になっている
- JSONの記述ミス
- HTMLタグの閉じ忘れ
などが原因になることがあります。
エラー内容をクリックすると、どの項目に問題があるのか確認できるため、修正後に再度テストを実行しましょう。
リッチリザルトとして表示されるとは限らない
リッチリザルトテストでエラーがなくても、必ず検索結果にFAQリッチリザルトが表示されるわけではありません。
現在はGoogleの仕様変更により、一般的な企業サイトではFAQリッチリザルトが表示されるケースは少なくなっています。
そのため、このテストは「リッチリザルトが表示されるか」を確認するものではなく、「構造化データが正しく実装されているか」を確認するためのツールとして利用するとよいでしょう。
まとめ:カスタム投稿FAQで運用の効率化とSEO対策を両立させよう
今回は、WordPressのカスタム投稿とタクソノミーを組み合わせた、実践的な「よくある質問(FAQ)」の構築方法を解説しました。
最後に、今回紹介した実装のポイントを振り返ってみましょう。
- カスタム投稿(faq)化: 管理画面が独立し、ブログ感覚でQ&Aを追加・編集できる
- ページ属性(menu_order)の活用: 手動での数値入力やプラグインでのドラッグ&ドロップで、並び替えが自由自在
- テンプレートパーツ化: 引数(category)を渡すだけで、サービスごとの出し分けや複数ページでの使い回しが1行で可能
- JSON-LDの自動出力: 検索結果にリッチリザルトを表示させ、SEOのクリック率(CTR)を高める
この方法は最初にコードを書く手間は少しだけかかりますが、一度このテンプレートを作ってしまえば、今後のサイト運用がかなり楽になります。
複数カテゴリをそれぞれのページで表示・非表示を選ぶたいサイトや、将来的にFAQが増える予定のあるコーポレートサイトにおすすめです。
ぜひ今回のコードを実際のテーマ開発で活用してみてください。
それでは、よきWordPress開発ライフを!
