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
bashnpm create vite@latest my-ui-app -- --template react-ts
cd my-ui-app
npm installCreate a Next.js project
bashnpx create-next-app@latest my-ui-app --typescript --tailwind --eslint
cd my-ui-appAdd Tailwind CSS to Vite
bashnpm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -pInstall dependencies
bashnpm install framer-motionUsage
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>
);
}