【React】アコーディオンメニューを作成する方法(アニメーションや複数対応、入れ子(ネストする)、ライブラリなど)
Reactでアコーディオンを作成する方法を紹介します。
まずはReactでシンプルなアコーディオンメニューの実装方法を紹介します。
その後、アニメーションの付け方や、より便利でサクッと作成できるライブラリの紹介をしています。
複数アコーディオンを設置したり、入れ子(ネストする)の場合のコードサンプルものせています。
Reactでシンプルなアコーディオンメニューを作成する方法
import { useState } from "react";
const Accordion = ({ title, children }) => {
const [isOpen, setIsOpen] = useState(false);
return (
<div className="accordion">
<button className="accordion-header" onClick={() => setIsOpen(!isOpen)}>
{title}
</button>
{isOpen && <div className="accordion-content">{children}</div>}
</div>
);
};
export default function App() {
return (
<div>
<Accordion title="メニュー 1">
<p>メニュー 1 の内容</p>
</Accordion>
<Accordion title="メニュー 2">
<p>メニュー 2 の内容</p>
</Accordion>
</div>
);
}
以下が簡単な解説です。
useState を使って開閉状態を管理します。。
button のクリックイベントでisOpen の状態を切り替えます。
isOpenがtrueの場合のみdivに内容を表示します。
Reactアコーディオンにアニメーションを追加する場合
インストール
npm install framer-motion
アニメーションを追加する場合、framer-motion を利用すると簡単です。
公式サイト: https://www.npmjs.com/package/framer-motion
実装
import { useState } from "react";
import { motion } from "framer-motion";
const Accordion = ({ title, children }) => {
const [isOpen, setIsOpen] = useState(false);
return (
<div className="accordion">
<button onClick={() => setIsOpen(!isOpen)}>{title}</button>
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: isOpen ? "auto" : 0, opacity: isOpen ? 1 : 0 }}
transition={{ duration: 0.3 }}
style={{ overflow: "hidden" }}
>
{children}
</motion.div>
</div>
);
};
簡単な解説です。
motion.div で height と opacity を変化させます。
animate を isOpen の状態に応じて変更します。
transition でアニメーションの速度を設定します。これで滑らかな開閉アニメーションが実現できます。
あとはプロパティをいろいろ変更してみればいろんなバリエーションのアニメーションが作成可能です。
Reactアコーディオンメニューを入れ子(ネスト)にする場合
アコーディオンをネストさせる場合の実装例です。
const NestedAccordion = ({ title, children }) => {
const [isOpen, setIsOpen] = useState(false);
return (
<div className="accordion">
<button onClick={() => setIsOpen(!isOpen)}>{title}</button>
{isOpen && <div className="nested">{children}</div>}
</div>
);
};
export default function App() {
return (
<NestedAccordion title="親メニュー">
<NestedAccordion title="子メニュー 1">
<p>子メニュー 1 の内容</p>
</NestedAccordion>
<NestedAccordion title="子メニュー 2">
<p>子メニュー 2 の内容</p>
</NestedAccordion>
</NestedAccordion>
);
}
親アコーディオンの children に別のアコーディオンを渡します。
入れ子になったアコーディオンが開閉可能です。
Reactのアコーディオンをより便利に簡単に使えるライブラリ
便利なライブラリを利用する方法です。
react-accessible-accordion
react-accessible-accordion は、WAI-ARIAに準拠したアクセシビリティ対応のアコーディオンライブラリです。
公式サイト: https://www.npmjs.com/package/react-accessible-accordion
インストール
npm install react-accessible-accordion
まずはnpm インストールします。
実装
import {
Accordion,
AccordionItem,
AccordionItemHeading,
AccordionItemButton,
AccordionItemPanel,
} from "react-accessible-accordion";
export default function App() {
return (
<Accordion allowMultipleExpanded allowZeroExpanded>
<AccordionItem>
<AccordionItemHeading>
<AccordionItemButton>メニュー 1</AccordionItemButton>
</AccordionItemHeading>
<AccordionItemPanel>
<p>メニュー 1 の内容</p>
</AccordionItemPanel>
</AccordionItem>
</Accordion>
);
}
react-accessible-accordion はキーボード操作に対応できるようになります。
アクセシビリティ対応(WAI-ARIA仕様に準拠)しています。
allowMultipleExpanded で複数の項目を開けます。
allowZeroExpanded で全て閉じることも可能です。
AccordionItemPanel でコンテンツ部分を明確に分けられます。
Material-UI(MUI)
Material-UI(MUI)の Accordion コンポーネントを利用する方法です。
Material-UI(MUI)の Accordion コンポーネントを使うと、GoogleのMaterial Designに沿ったアコーディオンを作成できます。
公式サイト: https://mui.com/material-ui/getting-started/
インストール
npm install @mui/material @emotion/react @emotion/styled
実装
import { Accordion, AccordionSummary, AccordionDetails } from "@mui/material";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
export default function App() {
return (
<div>
<Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
メニュー 1
</AccordionSummary>
<AccordionDetails>メニュー 1 の内容</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
メニュー 2
</AccordionSummary>
<AccordionDetails>メニュー 2 の内容</AccordionDetails>
</Accordion>
</div>
);
}
Googleの Material Design に準拠しています。
expandIcon で簡単にアイコンを設定します。
AccordionSummary でヘッダー、AccordionDetails で内容を明確に分離できます。
カスタマイズ性が高く、公式テーマとの統一が可能です。
Bootstrap
Bootstrap を使用すると、簡単にスタイリッシュなアコーディオンを作成できます。
react-bootstrap を使用する場合は、以下の手順でセットアップします。
公式サイト: https://react-bootstrap.netlify.app/docs/getting-started/introduction
インストール
npm install react-bootstrap bootstrap
まずはインストールします。
実装
import "bootstrap/dist/css/bootstrap.min.css";
import { Accordion } from "react-bootstrap";
export default function App() {
return (
<Accordion defaultActiveKey="0">
<Accordion.Item eventKey="0">
<Accordion.Header>メニュー 1</Accordion.Header>
<Accordion.Body>メニュー 1 の内容</Accordion.Body>
</Accordion.Item>
<Accordion.Item eventKey="1">
<Accordion.Header>メニュー 2</Accordion.Header>
<Accordion.Body>メニュー 2 の内容</Accordion.Body>
</Accordion.Item>
</Accordion>
);
}
react-bootstrap の Accordion コンポーネントを使用します。
defaultActiveKey="0″ で最初のアイテムを開いた状態にできます。
Bootstrap の 一貫したデザインになるため、Bootstrapベースのプロジェクトに最適です。
Chakra UI
Chakra UI を使用すると、アクセシブルでカスタマイズ可能なアコーディオンが作成できます。
公式サイト: https://www.chakra-ui.com/docs/get-started/installation
インストール
npm install @chakra-ui/react @emotion/react @emotion/styled framer-motion
実装
import { ChakraProvider, Accordion, AccordionItem, AccordionButton, AccordionPanel, Box } from "@chakra-ui/react";
import { AddIcon, MinusIcon } from "@chakra-ui/icons";
export default function App() {
return (
<ChakraProvider>
<Accordion allowMultiple>
<AccordionItem>
{({ isExpanded }) => (
<>
<h2>
<AccordionButton>
<Box flex="1" textAlign="left">
メニュー 1
</Box>
{isExpanded ? <MinusIcon /> : <AddIcon />}
</AccordionButton>
</h2>
<AccordionPanel>メニュー 1 の内容</AccordionPanel>
</>
)}
</AccordionItem>
<AccordionItem>
{({ isExpanded }) => (
<>
<h2>
<AccordionButton>
<Box flex="1" textAlign="left">
メニュー 2
</Box>
{isExpanded ? <MinusIcon /> : <AddIcon />}
</AccordionButton>
</h2>
<AccordionPanel>メニュー 2 の内容</AccordionPanel>
</>
)}
</AccordionItem>
</Accordion>
</ChakraProvider>
);
}
allowMultiple で複数のパネルを開くことが可能です。
AccordionButton に isExpanded を使って開閉アイコンを変更します。
スタイリッシュでカスタマイズしやすいです。
Radix UI
Radix UI はアクセシビリティ対応の軽量でアクセシビリティに優れた UI コンポーネントを利用できます。
公式サイト: https://www.radix-ui.com/themes/docs/overview/getting-started
インストール
npm install @radix-ui/react-accordion
実装
import * as Accordion from "@radix-ui/react-accordion";
import { useState } from "react";
export default function App() {
return (
<Accordion.Root type="single" collapsible>
<Accordion.Item value="item-1">
<Accordion.Header>
<Accordion.Trigger>メニュー 1</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>メニュー 1 の内容</Accordion.Content>
</Accordion.Item>
<Accordion.Item value="item-2">
<Accordion.Header>
<Accordion.Trigger>メニュー 2</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>メニュー 2 の内容</Accordion.Content>
</Accordion.Item>
</Accordion.Root>
);
}
軽量でカスタマイズ自由度が高いです。
type="single" で 単一選択、type="multiple" で 複数選択 が可能です。
collapsible で開閉をトグル可能です。
Reactアコーディオンメニューライブラリのまとめ
ライブラリ | インストール | 特徴 |
---|---|---|
react-accessible-accordion | react-accessible-accordion | WAI-ARIA対応、アクセシビリティに優れ、シンプル |
Material-UI(MUI) | @mui/material | Material Design準拠、カスタマイズ性が高い |
Bootstrap | react-bootstrap | Bootstrapベースのデザインと統一可能 |
Chakra UI | @chakra-ui/react | 軽量かつデザイン性が高く、簡単にカスタマイズ可能 |
Radix UI | @radix-ui/react-accordion | 軽量・柔軟なカスタマイズ性、アクセシビリティ対応 |
どのライブラリも便利ですが、デザインの統一性 や プロジェクトの要件 に合わせて選ぶと良いでしょう。
ライブラリなしで複数開閉するReactアコーディオン
複数のメニューを同時に開けるようにする方法です。
const MultiAccordion = ({ items }) => {
const [openItems, setOpenItems] = useState({});
const toggleItem = (index) => {
setOpenItems((prev) => ({ ...prev, [index]: !prev[index] }));
};
return (
<div>
{items.map((item, index) => (
<div key={index}>
<button onClick={() => toggleItem(index)}>{item.title}</button>
{openItems[index] && <div>{item.content}</div>}
</div>
))}
</div>
);
};
export default function App() {
return (
<MultiAccordion
items={[
{ title: "メニュー 1", content: "メニュー 1 の内容" },
{ title: "メニュー 2", content: "メニュー 2 の内容" },
]}
/>
);
}
openItems というオブジェクトで開閉状態を管理します。
toggleItem で開閉を切り替えます。
ReactでQ&Aアコーディオンを作成する方法
import { AiOutlinePlus, AiOutlineMinus } from "react-icons/ai";
import { useState } from "react";
const FAQ = ({ faq }) => {
const [showAnswer, setShowAnswer] = useState(false);
return (
<div className="faq" onClick={() => setShowAnswer((prev) => !prev)}>
<div>
<h5 className="faq__question">{faq.question}</h5>
<button className="faq__icon">
{showAnswer ? <AiOutlineMinus /> : <AiOutlinePlus />}
</button>
</div>
{showAnswer && <p className="faq__answer">{faq.answer}</p>}
</div>
);
};
export default FAQ;
React Iconsのプラスアイコンとマイナスアイコンを使っています。
React Iconsの使い方は簡単で以下が実装方法です。
開いているときと、閉じているときで状態管理をしたいので、
useStateを使います。
onClick={() => setShowAnswer((prev) => !prev)}
状態の切り替えはクリックイベントで前の状態を取得して、それを反転させます。
<div>
<h5 className="faq__question">{faq.question}</h5>
<button className="faq__icon">
{showAnswer ? <AiOutlineMinus /> : <AiOutlinePlus />}
</button>
</div>
{showAnswer && <p className="faq__answer">{faq.answer}</p>}
あとは、状態の変数であるshowAnswerによって、表示するものを変更します。
アイコン部分は、プラスとマイナスで切り替えるので、三項演算子を使います。
答えの部分は表示か非表示かなので、&&2つで記述します。showAnswerがtrueのときのみ次が実行されるようになります。
三項演算子や&&がわからない場合は、以下の記事を参考にしてみてください。