Animated FAQ
vault-hyperiux/animated-faqFreeComponent
Accessible accordion-style FAQ component with smooth GSAP animations
Install it
npx shadcn@latest add https://vault.hyperiux.com/r/animated-faq.jsonSource
1// Built using Hyperiux Vault: https://vault.hyperiux.com23"use client";45import React, {6 createContext,7 useCallback,8 useContext,9 useEffect,10 useId,11 useLayoutEffect,12 useMemo,13 useRef,14 useState,15} from "react";1617import gsap from "gsap";18import { ChevronDown, ChevronRight } from "lucide-react";1920const useIsomorphicLayoutEffect =21 typeof window !== "undefined" ? useLayoutEffect : useEffect;2223const FAQContext = createContext(null);24const FAQGroupContext = createContext(null);2526const useFAQContext = () => {27 const context = useContext(FAQContext);2829 if (!context) {30 throw new Error("FAQTitle and FAQContent must be used inside FAQWrapper.");31 }3233 return context;34};3536export function FAQGroup({37 children,38 allowMultiple = false,39 defaultOpenItems = [],40 value,41 onChange,42}) {43 const isControlled = Array.isArray(value);44 const [internalOpenItems, setInternalOpenItems] = useState(defaultOpenItems);4546 const openItems = isControlled ? value : internalOpenItems;4748 const toggleItem = useCallback(49 (itemId) => {50 const next = (() => {51 const isOpen = openItems.includes(itemId);5253 if (allowMultiple) {54 return isOpen55 ? openItems.filter((id) => id !== itemId)56 : [...openItems, itemId];57 }5859 return isOpen ? [] : [itemId];60 })();6162 if (!isControlled) {63 setInternalOpenItems(next);64 }6566 onChange?.(next);67 },68 [allowMultiple, isControlled, onChange, openItems]69 );7071 const contextValue = useMemo(72 () => ({73 allowMultiple,74 openItems,75 toggleItem,76 }),77 [allowMultiple, openItems, toggleItem]78 );7980 return (81 <FAQGroupContext.Provider value={contextValue}>82 {children}83 </FAQGroupContext.Provider>84 );85}8687export function FAQWrapper({88 children,89 className = "",90 titleClassName = "",91 contentClassName = "",92 iconClassName = "",93 iconSize = 18,94 iconStrokeWidth = 2,95 duration = 0.45,96 defaultOpen = false,97 controlledOpen,98 onToggle,99 itemId,100}) {101 const group = useContext(FAQGroupContext);102103 const generatedId = useId();104 const resolvedItemId = itemId ?? generatedId;105106 const isStandaloneControlled = typeof controlledOpen === "boolean";107 const [internalOpen, setInternalOpen] = useState(defaultOpen);108109 const isOpen = group110 ? group.openItems.includes(resolvedItemId)111 : isStandaloneControlled112 ? controlledOpen113 : internalOpen;114115 const contentOuterRef = useRef(null);116// … truncated
What it pulls in
npm packages
More from vault-hyperiux
All 115 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| 3D Portfolio Slider3d-portfolio-slider | vault-hyperiux | Components | Free | 3 deps | |
| Animated Formanimated-form | vault-hyperiux | Components | Free | 1 dep | |
| Animated Modalanimated-modal | vault-hyperiux | Components | Free | 1 dep | |
| Tabsanimated-tabs | vault-hyperiux | Components | Free | 1 dep | |
| Animated Toggleanimated-toggle | vault-hyperiux | Components | Free | no deps | |
| Arrow Fill Buttonarrow-fill-button | vault-hyperiux | Components | Free | 1 dep | |
| Block Transitionblock-transition | vault-hyperiux | Components | Free | 2 deps | |
| Blur Textblur-text | vault-hyperiux | Components | Free | 1 dep |
