FAQ Section

Frequently Asked Questions

import { FAQList } from "@/registry/zedUI/FAQ";

const faqs = [
  {
    question: "What makes Zed UI unique?",
    answer:
      "Zed UI stands out through its minimal design, powerful component library, and seamless integration options.",
  },
  {
    question: "Do the components work with dark mode?",
    answer:
      "Yes, all components are designed to work seamlessly with both light and dark modes.Yes, all components are designed to work seamlessly with both light and dark modes.Yes, all components are designed to work seamlessly with both light and dark modes.",
  },
  {
    question: "How can I contribute to Zed UI?",
    answer:
      "You can contribute by submitting issues, pull requests, or suggestions on our GitHub repository.",
  },
  {
    question: "Is Zed UI compatible with React?",
    answer:
      "Yes, Zed UI components are built with React and are fully compatible with React applications.",
  },
  {
    question: "Where can I find the documentation for Zed UI?",
    answer:
      "Documentation is available on our official website, providing detailed guides and examples for each component.",
  },
];

export default function FaqDemo() {
  return (
    <section className="flex justify-center items-center flex-col w-full py-10 md:py-20 md:px-10 ">
      <h2 className="text-2xl font-bold text-center mb-6">
        Frequently Asked Questions
      </h2>
      <FAQList items={faqs} />
    </section>
  );
}

Installation

npx shadcn@latest add https://zedui.vercel.app/r/faq.json
pnpm dlx shadcn@latest add https://zedui.vercel.app/r/faq.json
yarn dlx shadcn@latest add https://zedui.vercel.app/r/faq.json
bun x shadcn@latest add https://zedui.vercel.app/r/faq.json

Copy and paste the following code into your project.

components/FAQ.tsx
"use client";

import { useState, useRef } from "react";
import { motion, AnimatePresence } from "motion/react";
import { ChevronDown } from "lucide-react";
import { cn } from "@/src/lib/utils";

export interface FAQItem {
  question: string;
  answer: string;
}

interface FAQListProps {
  items: FAQItem[];
  className?: string;
  stagger?: boolean;
  delay?: number;
  animated?: boolean;
}

export function FAQList({
  items,
  className,
  stagger = true,
  delay,
  animated,
}: FAQListProps) {
  const ref = useRef(null);

  return (
    <div ref={ref} className={cn("space-y-2 w-full  ", className)}>
      {items.map((item, index) => (
        <SingleFAQItem
          key={index}
          question={item.question}
          answer={item.answer}
          index={index}
          delay={stagger ? delay : 0} // Use stagger delay if enabled
          animated={animated} // Pass stagger prop to control animation
        />
      ))}
    </div>
  );
}

function SingleFAQItem({
  question,
  answer,
  index,
  delay = 0.15,
  animated = true,
}: FAQItem & { index: number; delay?: number; animated?: boolean }) {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <motion.div
      initial={animated ? { opacity: 0, y: 10 } : false}
      animate={animated ? { opacity: 1, y: 0 } : false}
      transition={
        animated
          ? {
              duration: 0.3,
              delay: index * delay,
              ease: "easeOut",
            }
          : undefined
      }
      className={cn(
        "group rounded-lg border border-border ",
        "transition-colors duration-200 ease-in-out",
        isOpen ? "bg-card/30 shadow-sm" : "hover:bg-card/50"
      )}
    >
      <button
        type="button"
        onClick={() => setIsOpen(!isOpen)}
        className="flex w-full items-center justify-between gap-4 px-6 py-4 "
      >
        <h3
          className={cn(
            "text-left text-base font-medium transition-colors duration-200 ",
            "text-foreground/80",
            isOpen && "text-foreground"
          )}
        >
          {question}
        </h3>
        <motion.div
          animate={{
            rotate: isOpen ? 180 : 0,
            scale: isOpen ? 1.1 : 1,
          }}
          transition={{ duration: 0.3, ease: "easeInOut" }}
          className={cn(
            "shrink-0 rounded-full p-0.5 ",
            "transition-colors duration-200",
            isOpen ? "text-primary" : "text-muted-foreground"
          )}
        >
          <ChevronDown className="h-4 w-4" />
        </motion.div>
      </button>

      <AnimatePresence initial={false}>
        {isOpen && (
          <motion.div
            initial={{ height: 0, opacity: 0 }}
            animate={{
              height: "auto",
              opacity: 1,
              transition: {
                height: { duration: 0.4, ease: [0.04, 0.62, 0.23, 0.98] },
                opacity: { duration: 0.25, delay: 0.1 },
              },
            }}
            exit={{
              height: 0,
              opacity: 0,
              transition: {
                height: { duration: 0.3, ease: "easeInOut" },
                opacity: { duration: 0.25 },
              },
            }}
            className=""
          >
            <div className="border-t border-border/40 px-6 pb-4 pt-2">
              <motion.p
                initial={{ y: -8, opacity: 0 }}
                animate={{ y: 0, opacity: 1 }}
                exit={{ y: -8, opacity: 0 }}
                transition={{ duration: 0.3, ease: "easeOut" }}
                className="text-sm leading-relaxed text-muted-foreground"
              >
                {answer}
              </motion.p>
            </div>
          </motion.div>
        )}
      </AnimatePresence>
    </motion.div>
  );
}

Usage

import { FAQ } from '@/components/FAQ';
const faqItems = [
  {
    question: '...',
    answer: '...',
  },
  // Add more FAQ items as needed
];
<FAQ
  items={faqItems}
  stagger={true}
  delay={0.15}
  animated={true}
/>

Props

PropTypeDefault
animated?
boolean
true
delay?
number
0.15
stagger?
boolean
true
items?
{ question: string; answer: string; }[]
-