Create an Animated FAQs Component with styled-components, react-spring, and React Hooks
Tags: react, reactspring, styledcomponents, reacthooks
Published: 2019-08-30 20:49:15

---
title: Create an Animated FAQs Component with styled-components, react-spring, and React Hooks
published: true
tags:
  - react
  - react-spring
  - styled-components
  - react hooks
cover_image: https://thepracticaldev.s3.amazonaws.com/i/uz5865ign8aiipt8tynw.jpg
canonical_url: https://www.stoutlabs.com/blog/2019-08-28-animated-react-faqs-with-hooks-react-spring/
---

In this post, we are going to build something I recently created for a client site: an animated FAQs (Frequently Asked Questions) component. It's simple enough to write a tutorial about, while also showing some pretty powerful tools to use in your projects. This is going to be a long post, so let's get right to it!

Here's a quick demo of what we'll make:
[FAQs Demo](https://stoutlabs-faqs-demo.netlify.com/)

Note: To follow along with this tutorial, you need a React-based site set up and ready to edit. Based on the topic of this post, I will assume that you don't need help getting to that point. 😂 (I just used a [Gatsby.js](https://www.gatsby.org/) default starter for the demo.)

## Install Libraries

To create this FAQs component, we are going to make use of two outstanding React libraries, `react-spring` and `styled-components`:

- [react-spring](https://www.react-spring.io/) is a powerful and easy to implement animation library built for use within React. We're just barely making use of it in this post, but it's capable of extremely advanced animation sequences. Be sure to [check out their docs](https://www.react-spring.io/docs/) and [examples](https://www.react-spring.io/docs/hooks/examples).

- [styled-components](https://www.styled-components.com/) is an amazing CSS-in-JS library that I use with nearly every React project I work on. There are other similar solutions out there, and I have tried most of them more than once... but `styled-components` continues to be my favorite.

Let's install both of those now:

```sh
$ yarn add react-spring styled-components
```

Afterwards, you will likely need to configure `styled-components` to work with your React site. For example, in a Gatsby site we would need to install an additional Gatsby plugin, and modify the `gatsby-config.js` file. I wrote a full post on [using styled-components with Gatsby](https://alligator.io/gatsbyjs/using-styled-components-in-gatsbyjs/) at Alligator.io, if you're interested.

We are also going to make use of React's new `Hooks` feature, so be sure that you are using React version `16.8.0` or higher. (At the time of writing this post, React is at version `16.9.0`.)

## Create the Basic Component(s)

Let's first set up a new directory inside our project at `/src/components/Faqs/`. Inside this directory, let's create two new files:

### Faq.js

This file is a React component that functions as an individual FAQ question/answer pair.

#### /src/components/Faqs/Faq.js

```jsx
import React, { useState } from "react";

const Faq = props => {
  const { question, answer } = props;
  const [isOpen, toggleOpen] = useState(false);

  return (
    <div onClick={() => toggleOpen(!isOpen)}>
      <div className="faq-question">
        <span>
          Q: {question}
        </span>
      </div>

      <div 
        className="faq-answer" 
        style={isOpen ? { display: "block"} : { display: "none" }}
      >
        <span>
          A: {answer}
        </span>
      </div>
    </div>
  );
};

export default Faq;
```

As you can see, we are making use of the `useState` hook in React to track an open/closed state for this component. It doesn't really do much yet, but soon we'll animate the showing & hiding of the answer using `react-spring`!

### FaqsList.js

This file is just a simple React component that will function as a container to hold our list of FAQs:

#### /src/components/Faqs/FaqsList.js

```jsx
import React from "react";

import Faq from "./Faq";

// this data could come from anywhere
const faqsData = [
  { 
    question: "What does FAQ stand for?",
    answer: "Frequently Asked Question"
  },
  {
    question: "What is the best ice cream flavor?",
    answer: "Coffee with fudge ripple, or homemade strawberry."
  }
];

const FaqsList = () => {
  return (
    <div>
      {faqsData.map((faq, i) => (
        <Faq key={"faq_" + i} question={faq.question} answer={faq.answer} />
      ))}
    </div>
  );
};

export default FaqsList;
```

Note the `faqsData` array of FAQ objects above. This data could come from anywhere (your CMS, an API, etc), but for demo purposes I just hard-coded some data in.

Ok, now that we have our basic components set up... let's add the fun stuff: styles and animation!

## Styling With styled-components

Let's create some basic styles for our `FaqsList` and `Faq` components. Create a new `faq-styles.js` file in the same directory as our components, and insert this code:

#### /src/components/Faqs/faq-styles.js

```jsx
import styled from "styled-components";

export const StyledFaq = styled.div`
  cursor: pointer;
  margin: 0 0 10px;

  div.faq-question {
    font-size: 125%;
    font-weight: 800;
    margin: 0 0 5px;
  }

  div.faq-answer {
    background: #fff;
    overflow: hidden;

    span {
      display: block; 
      padding: 20px 10px;
    }
  }
`;

export const StyledFaqsList = styled.div`
  background: #efefef;
  margin: 20px 0;
  padding: 1rem;
`;
```

Notice how we're exporting each of these? This will allow us to import them from the component files we created above. This method will keep all of your FAQs styles all in one location, for easier customizing later on.

Note: This is my typical pattern when making "folder-based" components that I plan to re-use in other locations. _A lot of folks seem to think that styles must be within each component file when using `CSS-in-JS` solutions... but that is incorrect!_

### Adjust the Components

Let's adjust our `Faq.js` and `FaqsList.js` components to make use of these new styles:

#### /src/components/Faqs/Faq.js

```jsx
import React, { useState } from "react";

import { StyledFaq } from "./faqStyles";

const Faq = props => {
  const { question, answer } = props;
  const [isOpen, toggleOpen] = useState(false);

  return (
    <StyledFaq onClick={() => toggleOpen(!isOpen)}> 
      <div className="faq-question">
        <span>Q: {question}</span>
      </div>

      <div
        className="faq-answer"
        style={isOpen ? { display: "block" } : { display: "none" }}
      >
        <span>A: {answer}</span>
      </div>
    </StyledFaq> );
};

export default Faq;
```

All we did above was add an import statement for `StyledFaq`, and then swap out the outer `div` element with our imported styled component. Make sense?

Next, we'll do the same thing with the `FaqsList` component:

#### /src/components/Faqs/FaqsList.js

```jsx
import React from "react";

import Faq from "./Faq";
import { StyledFaqsList } from "./faqStyles";

const faqsData = [
  {
    question: "What does FAQ stand for?",
    answer: "Frequently Asked Question!",
  },
  {
    question: "What's the best ice cream flavor?",
    answer: "Coffee with fudge ripple, or homemade strawberry.",
  },
];

const FaqsList = () => {
  return (
    <StyledFaqsList> {faqsData.map((faq, i) => (
        <Faq key={"faq_" + i} question={faq.question} answer={faq.answer} />
      ))}
    </StyledFaqsList> );
};

export default FaqsList;
```

You should now have a basic styled FAQs list displaying, with each FAQ item showing/hiding the answer when clicked. _If yours isn't doing that, I'll post a link to the full source at the end⁠ — so don't panic!_ 😎

## Adding Animation With react-spring

Let's add some animation to this with `react-spring`! To keep it really simple for this post, we will just animate the showing/hiding of the answer portion of each FAQ when clicked.

_(And yes, my CSS warrior friends... we could do something like this with pure CSS ⁠— but I want to show usage of react-spring in this post!)_

But first, we need to add in a tiny npm package to help us measure the height of our answers. We need that info to tell `react-spring` what the height is when an answer is in the 'open' state. There's a few available options for this, but I'm going to use [react-resize-aware](https://www.npmjs.com/package/react-resize-aware) - since it has an easy-to-use hooks-based solution.

Add it to your project, like usual:

```bash
$ yarn add react-resize-aware
```

Now we just need to edit the `Faq.js` component to add the animations. Below is the updated code:

#### /src/components/Faqs/Faq.js

```jsx
import React, { useState } from "react";
import { useSpring, animated } from "react-spring";
import useResizeAware from "react-resize-aware";

import { StyledFaq } from "./faqStyles";

const Faq = props => {
  const { question, answer } = props;
  const [isOpen, toggleOpen] = useState(false);
  const [resizeListener, { height }] = useResizeAware(); 
  const animProps = useSpring({ 
    height: isOpen ? height : 0, 
    opacity: isOpen ? 1 : 0, 
  });
  
  return (
    <StyledFaq onClick={() => toggleOpen(!isOpen)}>
      <div className="faq-question">
        <span>Q: {question}</span>
      </div>

      <animated.div className="faq-answer" style={{ ...animProps }}> 
        <span style={{ position: "relative" }}> 
          {resizeListener} A: {answer}
        </span>
      </animated.div> </StyledFaq>
  );
};

export default Faq;
```

To explain a little more, we did the following things above:

- Imported the two packages we already installed, `react-spring` and `react-resize-aware`. We destructured `useSpring` and `animated` from `react-spring` so they're easier to use.
- Created a new variable for our animation configuration settings, using the `useSpring` Hook from `react-spring`. Notice that we set initial values of `0` for the opacity and height, and then our measured `height` value is used to set the height when the answer is shown. (And of course, opacity is set to 1.)
- Converted the `faq-answer` div into a react-spring `animated.div` element, and spread the values of `animProps` out into the `styles` prop.
- Added a `position: relative` style to the answer's inner `span` tag. This is required by `react-resize-aware` to properly measure the element on load. (See next item.)
- Added a `resizeListener` into our answer's inner span. This is part of `react-resize-aware`, and it measures the answer's height when loaded. (It's essentially an invisible div that returns its width and height via a custom React Hook... so it works perfectly in our stateless component!)

Go ahead and give it a try, if you haven't already. Each FAQ item should now animate open when clicked, and should animate back to closed if clicked again. Pretty cool, huh? You can now re-use this component in any of your sites, and you only need to edit the styles/animations to fit your needs.

## Final Thoughts

We are finished! I hope that helps a few of you out there, and maybe even gives you some ideas to try on your own!

### Preview/Download Source:

Demo here: [https://stoutlabs-faqs-demo.netlify.com/](https://stoutlabs-faqs-demo.netlify.com/)

Demo Source on Github: [https://github.com/stoutlabs/demo-spring-hooks](https://github.com/stoutlabs/demo-spring-hooks).

I am planning to start writing more... so I'll catch you on the next post!  💜