🎨

アコーディオン付きFAQを作ってみた!【HubSpot × React DAY10】


目次

はじめに 🌿

FAQリストって、たくさんあると見づらくなりがち…。

アコーディオンを開こうとするちゃろさん(困り顔)
アコーディオン形式で「質問をクリックすると回答が開く」UIなら、ユーザーも編集者も嬉しい!
今回は HubSpot の Reactモジュール + islands + RepeatedFieldGroup を使って、動きのあるFAQモジュールを作ってみたよ✨

react-day10


この記事でわかること ✏️

  • アコーディオン形式のFAQリストを実装する方法

  • useState の状態管理と islands の関係

  • RepeatedFieldGroup で自由にQAを追加する方法


結論 🎯

HubSpot CMS × React で、アコーディオンUIをつけたいなら islands が必須!
さらに RepeatedFieldGroup を使えば、質問・回答を管理画面から自由に増やせて便利💡


やり方 🛠️

Step1:フィールド定義で RepeatedFieldGroup を使おう ✍️

indes.jsx
export const fields = (
  <ModuleFields>
    <RepeatedFieldGroup
      name="faqs"
      label="FAQリスト"
      occurrence={{ min: 1, max: 20, default: 3 }}
      default={[
        {
          question: 'サンプル質問:ちゃろさんって?',
          answer: '<p>スコティッシュのかわいい女の子!</p>',
        },
      ]}
    >
      <TextField name="question" label="質問" default="サンプル質問" required />
      <RichTextField name="answer" label="回答(リッチテキスト)" default="<p>サンプル回答</p>" />
    </RepeatedFieldGroup>
  </ModuleFields>
);

 

Step2:island 対応のクライアントコンポーネントを作成 📦

 

islands/FAQAccordionIsland.jsx
import React, { useState } from 'react';

/**
 * アコーディオン表示のロジックを持つコンポーネント
 */
export default function FAQAccordionIsland({ faqs }) {
  const [openIndex, setOpenIndex] = useState(null);
  const toggle = (i) => setOpenIndex(openIndex === i ? null : i);

  return (
    <section className="faq-list" style={{ maxWidth: 800, margin: '0 auto' }}>
      {faqs?.map((faq, index) => (
        <div key={index} style={{ marginBottom: 16, border: '1px solid #ddd', borderRadius: 8, overflow: 'hidden' }}>
          <h3
            onClick={() => toggle(index)}
            style={{ margin: 0, padding: 12, cursor: 'pointer', backgroundColor: '#f7f7f7' }}
            aria-expanded={openIndex === index}
          >
            {faq.question}
          </h3>
          {openIndex === index && (
            <div style={{ padding: 12, borderTop: '1px solid #eee', backgroundColor: '#fff' }}>
              <div dangerouslySetInnerHTML={{ __html: faq.answer }} />
            </div>
          )}
        </div>
      ))}
    </section>
  );
}

// ポイント:モジュール本体から ?island付きでインポートすると、
// このコンポーネントがクライアント上でハイドレートされるようになります 💡 

 

📝

ここで使っている仕組みは?

  • useState で「開いている質問のインデックス(番号)」を記録してるよ
    たとえば openIndex = 2 なら、3つ目のQAだけ開いている状態✨

  • toggle(i) 関数で、同じ質問をもう一度クリックしたら閉じる・別の質問をクリックしたらそっちが開くという切り替えロジックになってる

  • openIndex = i を使って、「この質問が開いてるかどうか」を判定してるのがポイント!

  • islands を使ってるのは、この動き(状態の切り替え)はクライアント側の JavaScript で動かさないといけないから💡



Step3:Island でクライアント側にハイドレートしよう 🏝️

index.jsx
import { Island } from '@hubspot/cms-components';
// Island コンポーネントとして切り出した部分を読み込む(?island がポイント)
import FAQAccordionIsland from '../../islands/FAQAccordionIsland.jsx?island';

/**
 * HubSpot CMS モジュール: Island を使ってクライアントコンポーネントをハイドレート
 */
export function Component({ fieldValues }) {
    return (
      <Island module={FAQAccordionIsland} faqs={fieldValues.faqs} />
    );
  }

 


注意ポイント ⚡


まとめ ✨

FAQをアコーディオン表示にするだけで、見やすさ&操作性がグッとUP!
HubSpot CMS × React × islands の組み合わせで、動きのあるUIがどんどん作れるよ。
「よくある質問」や「用語集」など、応用もいろいろできるから、ぜひ活用してみてね💡

アコーディオンFAQを指さしながらにっこり笑うちゃろさん


あわせて読みたい 📖

Reina

Written by Reina

HubSpot CMS (現 Content Hub) をメインに 気になったことをまとめます #vibe coding

💬 コメントしてみる?