Categories
React Bootstrap

React Bootstrap — Accordion

Spread the love

React Bootstrap is one version of Bootstrap made for React.

It’s a set of React components that have Bootstrap styles.

In this article, we’ll look at how to work with React Bootstrap’s accordions in our React components.

Accordion

Accordions let us restrict card components to open only one at a time.

To use it, we can write:

import React from "react";
import Accordion from "react-bootstrap/Accordion";
import Card from "react-bootstrap/Card";
import Button from "react-bootstrap/Button";
import "bootstrap/dist/css/bootstrap.min.css";

export default function App() {
  return (
    <>
      <Accordion defaultActiveKey="0">
        <Card>
          <Card.Header>
            <Accordion.Toggle as={Button} variant="link" eventKey="0">
              title 1
            </Accordion.Toggle>
          </Card.Header>
          <Accordion.Collapse eventKey="0">
            <Card.Body>body 1</Card.Body>
          </Accordion.Collapse>
        </Card>
        <Card>
          <Card.Header>
            <Accordion.Toggle as={Button} variant="link" eventKey="1">
              title 2
            </Accordion.Toggle>
          </Card.Header>
          <Accordion.Collapse eventKey="1">
            <Card.Body>body 2</Card.Body>
          </Accordion.Collapse>
        </Card>
      </Accordion>
    </>
  );
}

We imported the Accordion and Card components.

It has the defaultActionKey is the key for the default card.

Accordion.Toggle is the toggle to open the card.

as is the prop to let us specify what component to render the toggle as.

variant is the style that we want to render the toggle as.

eventKey is the key for the given card.

Fully Collapsed State

If we don’t specify the defaultActiveKey , then all cards will be collapsed.

For example, we can write:

import React from "react";
import Accordion from "react-bootstrap/Accordion";
import Card from "react-bootstrap/Card";
import Button from "react-bootstrap/Button";
import "bootstrap/dist/css/bootstrap.min.css";

export default function App() {
  return (
    <>
      <Accordion>
        <Card>
          <Card.Header>
            <Accordion.Toggle as={Button} variant="link" eventKey="0">
              title 1
            </Accordion.Toggle>
          </Card.Header>
          <Accordion.Collapse eventKey="0">
            <Card.Body>body 1</Card.Body>
          </Accordion.Collapse>
        </Card>
        <Card>
          <Card.Header>
            <Accordion.Toggle as={Button} variant="link" eventKey="1">
              title 2
            </Accordion.Toggle>
          </Card.Header>
          <Accordion.Collapse eventKey="1">
            <Card.Body>body 2</Card.Body>
          </Accordion.Collapse>
        </Card>
      </Accordion>
    </>
  );
}

Make the Entire Header Clickable

We can make the entire header clickable by rendering the header as a Card.Header .

For instance, we can write:

import React from "react";
import Accordion from "react-bootstrap/Accordion";
import Card from "react-bootstrap/Card";
import "bootstrap/dist/css/bootstrap.min.css";

export default function App() {
  return (
    <>
      <Accordion>
        <Card>
          <Card.Header>
            <Accordion.Toggle as={Card.Header} variant="link" eventKey="0">
              title 1
            </Accordion.Toggle>
          </Card.Header>
          <Accordion.Collapse eventKey="0">
            <Card.Body>body 1</Card.Body>
          </Accordion.Collapse>
        </Card>
        <Card>
          <Card.Header>
            <Accordion.Toggle as={Card.Header} variant="link" eventKey="1">
              title 2
            </Accordion.Toggle>
          </Card.Header>
          <Accordion.Collapse eventKey="1">
            <Card.Body>body 2</Card.Body>
          </Accordion.Collapse>
        </Card>
      </Accordion>
    </>
  );
}

We make the part outside the text clickable by changing the as to Card.Header .

Custom Toggle

We can create our custom toggle component with the useAccordionToggle hook.

It takes the eventKey and a callback that runs when the roggle is clicked.

For instance, we can write:

import React from "react";
import Accordion from "react-bootstrap/Accordion";
import Card from "react-bootstrap/Card";
import { useAccordionToggle } from "react-bootstrap/AccordionToggle";
import "bootstrap/dist/css/bootstrap.min.css";

function CustomToggle({ children, eventKey }) {
  const decoratedOnClick = useAccordionToggle(eventKey, () =>
    console.log("toggle clicked")
  );

  return (
    <button
      type="button"
      style={{ backgroundColor: "orange" }}
      onClick={decoratedOnClick}
    >
      {children}
    </button>
  );
}

export default function App() {
  return (
    <>
      <Accordion>
        <Card>
          <Card.Header>
            <CustomToggle eventKey="0">title 1</CustomToggle>
          </Card.Header>
          <Accordion.Collapse eventKey="0">
            <Card.Body>body 1</Card.Body>
          </Accordion.Collapse>
        </Card>
        <Card>
          <Card.Header>
            <CustomToggle eventKey="0">title 2</CustomToggle>
          </Card.Header>
          <Accordion.Collapse eventKey="1">
            <Card.Body>body 2</Card.Body>
          </Accordion.Collapse>
        </Card>
      </Accordion>
    </>
  );
}

We have the CustomToggle component which we created ourselves.

It takes the children and eventKey props.

children lets us display content that we pass inside the component tags.

useAccordingToggle returns a click handler that opens the card with the given eventKey .

The 2nd argument is a callback that lets us run anything when the toggle is clicked.

Then we can use the CustomToggle in place of the toggle that comes with React Bootstrap to toggle our cards.

Custom Toggle with Expansion Awareness

We can also check if the toggle has expanded the card when it’s clicked.

To do that, we use the context API to listen to the accordion to the AccordionContext .

For instance, we can write:

import React from "react";
import Accordion from "react-bootstrap/Accordion";
import AccordionContext from "react-bootstrap/AccordionContext";
import Card from "react-bootstrap/Card";
import { useAccordionToggle } from "react-bootstrap/AccordionToggle";
import "bootstrap/dist/css/bootstrap.min.css";

function CustomToggle({ children, eventKey, callback }) {
  const currentEventKey = React.useContext(AccordionContext);

const decoratedOnClick = useAccordionToggle(
    eventKey,
    () => callback && callback(eventKey)
  );

const isCurrentEventKey = currentEventKey === eventKey;

return (
    <button
      type="button"
      style={{ backgroundColor: isCurrentEventKey ? "orange" : "pink" }}
      onClick={decoratedOnClick}
    >
      {children}
    </button>
  );
}

export default function App() {
  return (
    <>
      <Accordion>
        <Card>
          <Card.Header>
            <CustomToggle eventKey="0">title 1</CustomToggle>
          </Card.Header>
          <Accordion.Collapse eventKey="0">
            <Card.Body>body 1</Card.Body>
          </Accordion.Collapse>
        </Card>
        <Card>
          <Card.Header>
            <CustomToggle eventKey="1">title 2</CustomToggle>
          </Card.Header>
          <Accordion.Collapse eventKey="1">
            <Card.Body>body 2</Card.Body>
          </Accordion.Collapse>
        </Card>
      </Accordion>
    </>
  );
}

We get the eventKey prop with our CustomToggle so that we can compare to the currentEventKey that we have in our context.

Then we have the useAccordionToggle as we did with the previous example.

It returns a click handler that we pass into onClick to expand the card.

isCurrentKey has the result of comparing the eventKey of the toggle with the one from the context which has the one that’s opened.

So we can use that to style the toggle the way we like when the card is open or closed.

Conclusion

Accordions let us restrict card components to open only one at a time.

We can customize the toggle and the card content.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *