【Next.js】よくある質問(FAQ)アコーディオンの作り方を解説

公開日: 2026年9月11日 · 読了目安 11 分

Next.js のランディングページに、開閉できる FAQ(よくある質問)アコーディオンを置く方法を解説します。details/summary、1つだけ開く制御、完成セクションの流用の3通り。読まれない FAQ の理由と、公開前の失敗もまとめます。

先に結論:FAQ は「閉じた見出し」が仕事をする

要約すると、Next.js の FAQ を機能させるには、答えの長さより先に、閉じた状態の質問が見えることが必要です。全部開いた長文はヘルプセンターです。アコーディオンで5〜8問、1行の質問が並ぶ。この形が見えると、同じ文章でも料金の直後で読まれます。

作り方は3つあります。HTML の details と summary だけで開閉する。state で同時に開く数を1つに制限する。すでに完成している FAQ セクションを自分のページに載せる。専用の FAQ ライブラリから入る必要はありません。

この記事を読み終える頃には、LP の末尾に置けるよくある質問を1本出せるようになります。

FAQ が読まれない3つの理由

よくある質問をページ最下部に、答えまで全部出したまま置く。悪いわけではありません。ただ、スクロールしてきた人が「自分の不安がここにあるか」を1秒で判断できないと、問い合わせボタンまで届きません。

1つ目は、質問が長すぎることです。「当サービスのご利用にあたってお客様から多く寄せられるお問い合わせについて」は、見出しではなく前置きです。閉じた行は、訪問者が口にする言葉にします。「無料で試せますか」「解約はいつでもできますか」で足ります。

2つ目は、答えを全部開いていることです。8問すべてが開いていると、アコーディオンではなく記事です。料金表のあとに長文が続くと、数字の印象が薄れます。

3つ目は、場所が悪いことです。フッターの手前に突然置くと、まだ比較していない人に「よくある質問」だけが当たります。価値と料金のあと、最終 CTA の直前が扱いやすいです。

完成イメージ:この記事で作る FAQ

目指す見た目は次のとおりです。

見出しは「よくある質問」でよい。説明文は1行まで。その下に質問が縦に並ぶ。閉じた状態では質問と、右(または左)の開閉アイコンだけ。1つ開くと、短い段落が下に出る。同時に開くのは1つ。スマホでは質問が折り返しても、タップ領域が潰れない。

検索用の巨大なヘルプセンターは扱いません。まずは、LP の不安を5〜8問で潰す画面です。

作り方は3つある

同じ完成形でも、手数は分かれます。

方法1は details / summary です。JavaScript は不要です。サーバーコンポーネントのまま置けます。同時に複数開きます。

方法2は、開いている value を1つ持つアコーディオンです。"use client" が必要です。キーボード操作まで揃えるなら Radix か shadcn/ui の Accordion が早いです。

方法3は、余白・開閉・見出し配置まで組んである FAQ セクションを自分のアプリに載せる方法です。アイコンの動きを一から決めなくて済みます。質問と答えの差し替えが仕事になります。100問のサポートサイト、カテゴリ検索が必要なドキュメントには向きません。

迷ったら、まず方法1で5問出して公開し、同時開きが気になったら方法2か3に進むのが早いです。

方法1:details と summary だけで開閉する

サーバーコンポーネントのまま始められます。ブラウザ標準の details は、スクリーンリーダーとキーボードの基本が付いてきます。見た目は Tailwind で枠線と余白を足します。

ポイントは summary を1行に保つこと

summary に2文入れると、閉じた一覧が読みにくくなります。質問は句点なしの短文。答えは details の中の p に1〜3文。リンクが必要なら答えの中だけです。

components/Faq.tsx
const items = [
  {
    q: "無料で試せますか?",
    a: "ライブプレビューはカード不要です。ソースのコピーは日本時間で1日3回まで無料です。",
  },
  {
    q: "Next.js の既存プロジェクトに載せられますか?",
    a: "React と Tailwind 前提のセクションです。足りない npm 依存は package.json に足します。",
  },
];

export function Faq() {
  return (
    <section className="mx-auto max-w-2xl px-6 py-24">
      <h2 className="text-3xl font-semibold">よくある質問</h2>
      <div className="mt-10 divide-y divide-zinc-200 border-y">
        {items.map((item) => (
          <details key={item.q} className="group py-4">
            <summary className="cursor-pointer list-none pr-8 text-left font-medium">
              {item.q}
            </summary>
            <p className="mt-3 text-sm leading-relaxed text-zinc-600">{item.a}</p>
          </details>
        ))}
      </div>
    </section>
  );
}

この段階で足らないもの

方法1は、複数の details を同時に開けます。料金の不安を1つ読んだら他は閉じたい、という LP なら方法2です。chevron の回転も、方法1では CSS の group-open で足せます。動きそのものを売りにしたいなら、方法3の方が速いです。

方法2:同時に開く数を1つに制限する

LP の FAQ は、比較表ではなく「今の不安を1つ潰す」場所です。Next.js では、FAQ だけ "use client" にします。ページ全体をクライアントコンポーネントにする必要はありません。

弱い指定と強い指定

弱い例は、各質問に useState を持ち、全部独立して開くことです。方法1と同じ挙動になります。

強い例は、開いている id を1つだけ持つことです。同じボタンをもう一度押したら閉じる。これで視線が1問に残ります。

components/FaqExclusive.tsx
"use client";

import { useState } from "react";

const items = [
  { id: "free", q: "無料プランはありますか?", a: "閲覧とプレビューは無料です。コピー枠は1日3回です。" },
  { id: "stack", q: "shadcn/ui と一緒に使えますか?", a: "使えます。FAQ はセクションとして足し、既存のトークンに色を寄せます。" },
];

export function FaqExclusive() {
  const [open, setOpen] = useState<string | null>("free");

  return (
    <section className="mx-auto max-w-2xl px-6 py-24">
      <h2 className="text-3xl font-semibold">よくある質問</h2>
      <div className="mt-10 divide-y">
        {items.map((item) => {
          const isOpen = open === item.id;
          return (
            <div key={item.id} className="py-4">
              <button
                type="button"
                aria-expanded={isOpen}
                onClick={() => setOpen(isOpen ? null : item.id)}
                className="w-full text-left font-medium"
              >
                {item.q}
              </button>
              {isOpen ? (
                <p className="mt-3 text-sm text-zinc-600">{item.a}</p>
              ) : null}
            </div>
          );
        })}
      </div>
    </section>
  );
}

shadcn/ui の Accordion を使うとき

キーボードの矢印移動、type="single"、アニメーションまで揃えるなら、自前の button より Accordion の方が早いです。役割はプリミティブです。左見出し+右リスト、暗い背景、開閉のイージングまで含めた「LP の FAQ セクション」ではありません。枠が欲しいだけなら shadcn。完成した塊が欲しいなら方法3です。

方法3:完成している FAQ セクションを載せる

左に見出し、右にアコーディオン、開くときに答えが下から伸びる。暗い背景で料金のあとに続く。こうした塊は、方法2の state だけでは半日かかることがあります。

その場合は、ブラウザで実際の FAQ を見てから、React と Tailwind でできたセクションを自分の Next.js に載せる方法があります。DesignLayer のカタログは、その置き場です。閲覧にカードは不要です。https://design-layer.com/components

向き不向きを先に言います。SaaS、ツール、サブスクの LP に向きます。多言語で100問あるサポート、ログイン後のヘルプには向きません。そこは検索付きのドキュメントです。

載せるときの仕事は3つです。ライブで開閉のテンポを見る。ソースを自分のリポジトリに置く。質問・答え・見出しだけ差し替える。アイコンと余白を一から作る必要はありません。

無料のソースコピーは日本時間で1日3回です。今日 FAQ 1本、で足ります。料金表は https://design-layer.com/articles/nextjs-pricing-table-guide 、ヒーローは https://design-layer.com/articles/nextjs-animated-hero-section-guide です。FAQ で強い入場アニメーションを足さない。読む場所です。

公開前チェックリスト

動きが付いたあとに見るのは、次の5点です。

1. 閉じた状態で、質問がスキャンできるか。答えの抜粋を summary に入れない。

2. 最初から全部開いていないか。1問だけ開いておくのはよいです。料金や無料枠の不安が先頭向きです。

3. スマホで summary / button の高さが 44px 以上あるか。罫線の隙間だけをタップ領域にしない。

4. 構造化データ(FAQPage)を付けるなら、画面に出ている質問と答えと一致させる。隠した文を JSON-LD だけに書かない。

5. "use client" を page.tsx 全体に書いていないか。FAQ だけクライアントに切り出します。

よくある失敗

失敗1:質問を社内用語で書く。「Copy for Cursor のクォータは?」より「1日何回コピーできますか?」の方が、検索してきた人の言葉です。

失敗2:答えで新しい売り文句を書く。FAQ の仕事は不安を消すことです。ヒーローのコピーの再掲ではありません。

失敗3:20問並べる。LP では読まれません。広告と料金で実際に聞かれることだけ残し、残りはドキュメントへリンクします。

よくある質問

実装で繰り返し出る質問です。

Next.js で FAQ アコーディオンを作るには何が必要ですか?

最初は details と summary で足ります。同時に1つだけ開きたいなら、FAQ 用のクライアントコンポーネントが1つ。余白とモーションまで含めた完成形が欲しいなら、完成セクションです。

質問はいくつが適切ですか?

LP では5〜8問が扱いやすいです。3問でも構いません。12問以上は、別ページのヘルプに分けます。

サーバーコンポーネントのまま置けますか?

details なら可能です。開いている id を state で持つなら、FAQ のファイルだけ "use client" にします。

shadcn/ui の Accordion では足りませんか?

開閉の部品としては足ります。見出しとの2カラム、背景、開くタイミングの演出は自分で足します。プリミティブと、完成した FAQ セクションは役割が違います。

SEO のために全部開いておいた方がよいですか?

答えのテキストは HTML に入っていれば、閉じていても問題になりにくいです。details は最初から DOM に答えがあります。クライアントで開いたときだけ描画する場合は、クローラに載るか別途確認します。見せ方と JSON-LD を二重に嘘にしない方が安全です。

FAQPage の構造化データは必要ですか?

検索結果に FAQ の折りたたみが付くことがあります。付けるなら、ページに表示している質問と答えだけです。他サイトのコピーを JSON に載せない。

アクセシビリティはどうしますか?

開閉は button か summary にする。div の onClick だけにしない。aria-expanded を合わせる。アイコンは装飾なら aria-hidden。質問は見出しレベルを飛ばさない。

既存の page.tsx のどこに置けばよいですか?

料金表のあと、最終 CTA またはフッターの前です。ヒーローの直後に置くと、まだ欲しくない人に不安の一覧だけが当たります。数字を見た直後が扱いやすいです。料金表の作り方は https://design-layer.com/articles/nextjs-pricing-table-guide です。

【Next.js】よくある質問(FAQ)アコーディオンの作り方を解説 | DesignLayer