A 10 Line React Accordion Using an HTML Tag!
Tags: beginners, javascript, react, webdev
Published: 2019-12-15 09:31:18

I recently came across a use case in which I needed an accordion component in my react application. I had a few options like using [Material UI](https://v1.material-ui.com/demos/expansion-panels/), some open source components [this](https://reactjsexample.com/tag/accordion/) and [this](https://codepen.io/adamaoc/pen/wBGGQv). 

>In my use case set of accordion options were dynamic, and they themselves carry information whether they should be open or not by default from the parent.

I tried a few components but after a day or two, I always had to go back and remove the component or add some behavior myself. In most components, parents keep track of open sections and children themselves have to use some change handler to inform parent for changing open sections.

So, I created one myself with just a few lines of code and without using any 3rd party library.

![Accordion](https://thepracticaldev.s3.amazonaws.com/i/er2wyoakxvdiru6qxo9l.png)

Let me introduce to you HTML's `<detail>` tag.
This tag carries in itself the on-demand show/hide feature which is the core of accordion. We will use a set of these to create our component and it will save us tens of lines of code to handle the opening and closing behavior ourselves. This can be used to create our interactive widget which will have an `open` prop that decides whether to show or hide the content. All of this being a *no-JavaScript* widget.

Helper link: [detail tag](https://www.w3schools.com/tags/tag_details.asp)

Note: The details tag is not supported in Internet Explorer.

There's one more tag that we will need i.e `<summary>`. This tag defines a heading that can be clicked to show or hide the content section. It also comes with a built-in toggling arrow which we can customize.

Here's how it looks:

![details plus summary tag](https://thepracticaldev.s3.amazonaws.com/i/ev9wq8494jw4roxuxvqp.png)


## The Accordion Component

```
function Accordion({ children: options }) {
  const toOpen = options.props.open ? true : null;
  return options.map(options => (
    <details open={toOpen}>{options}</details>
  ));
}
```

Two things to note here:
1. Children carry property whether they want to be open or not. You can change it as peruse.
2. In JSX, props compile to plain javascript object before they are used an attribute to plain HTML, hence we return null to disallow it from being added to HTML. A detailed explanation:

{% stackoverflow 41798074 %} 

It will be used like this:
```
<Accordion>
   ...Options
</Accordion>
```

## The Option Component
This will be the content of each section in the accordion. It uses summary tag to show the label (heading) of the section which is clickable. It renders children which for each section without any javascript.


```
function Option({ label, children }) {
  return (
    <React.Fragment>
      <summary>
        {label}
      </summary>
      {children}
    </React.Fragment>
  );
}
```

I used it like this, 

```
<Accordion>
 {this.getOptions(status)}
</Accordion>
```
Some code to get options list:

```
function getOptions(status) {
 // status decides which options to return with what open flag
return [
     ...moreOptions, 
     <Option label="heading" open={status === "status1"}>
        <Component1
          someProp={someProp}
         />
      </Option>
    ]

}
```

Here's the final output with some applied CSS.



{% codesandbox vigorous-bohr-c97ri runonclick=1 %}

>Note: To disable the default icon in summary component:

```
 summary::-webkit-details-marker {
     display: none
 }
```

Now feel free to play around with it. It is often advisable not to download and keep 3rd party libraries for simple use cases, It took me 5 tries to decide that I can just do it in less than 10 lines of code for which I kept adding 3rd party components. Simple functionalities can often be extracted out and used independently.

Thanks for making it to the end of the post, you are awesome! 
Please share it if you found it helpful or drop me a hello on [twitter](https://twitter.com/Dpkahuja) :)