Skip to content
FreeBlocks

Frequently Asked Questions

Use this component on pricing pages, product pages, resource pages, and checkout flows to answer common objections in a compact block.

ReactTailwind CSSMotionAccordion

Frequently asked questions

Elegant accordions with clean motion.

Overview

An elegant FAQ section with animated accordion rows and minimal styling.

Framework

React

Styling

Tailwind CSS

Animation

Motion

Create a Vite React project

bash
npm create vite@latest my-ui-app -- --template react-ts
cd my-ui-app
npm install

Create a Next.js project

bash
npx create-next-app@latest my-ui-app --typescript --tailwind --eslint
cd my-ui-app

Add Tailwind CSS to Vite

bash
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

Install dependencies

bash
npm install framer-motion

Usage

tsx
<FaqAccordion />

Frequently Asked Questions.tsx

tsx
"use client";

import { useState } from "react";
import { AnimatePresence, motion } from "framer-motion";

const items = [
  ["What is this component?", "A minimal FAQ section with animated open states."],
  ["Can I customize it?", "Yes. Replace the array with your own questions."],
  ["Does it need a UI library?", "No. It only uses React, Tailwind, and Motion."],
  ["Is it responsive?", "Yes. The layout remains compact on mobile screens."],
];

export function FaqAccordion() {
  const [open, setOpen] = useState(0);

  return (
    <section className="rounded-xl border border-white/10 bg-black p-5 text-white">
      <div className="mx-auto max-w-xl text-center">
        <h3 className="text-2xl font-black">Frequently asked questions</h3>
        <p className="mt-2 text-xs text-zinc-400">Elegant accordions with clean motion.</p>
      </div>
      <div className="mx-auto mt-5 max-w-xl space-y-2">
        {items.map(([question, answer], index) => (
          <button
            key={question}
            type="button"
            onClick={() => setOpen(open === index ? -1 : index)}
            className="w-full rounded-lg border border-white/10 bg-white/[0.04] p-3 text-left"
          >
            <div className="flex items-center justify-between gap-3 text-sm font-semibold">
              {question}
              <span>{open === index ? "-" : "+"}</span>
            </div>
            <AnimatePresence initial={false}>
              {open === index && (
                <motion.p
                  initial={{ height: 0, opacity: 0 }}
                  animate={{ height: "auto", opacity: 1 }}
                  exit={{ height: 0, opacity: 0 }}
                  className="overflow-hidden pt-2 text-xs leading-relaxed text-zinc-400"
                >
                  {answer}
                </motion.p>
              )}
            </AnimatePresence>
          </button>
        ))}
      </div>
    </section>
  );
}