Collapsible

Interactive component to show and hide content.

Prompt engineering is the practice of creating clear and effective instructions so an AI model understands exactly what you want. Common techniques include:

  • Few-shot: Providing examples to guide the model.
  • Chain-of-thought: Asking the model to reason step by step.
  • System prompts: Defining the assistant's role and behavior.
  • Temperature: Adjusting creativity vs. precision.
You are an expert assistant specialized in...
1'use client';
2
3import { Collapsible } from '@/components/ui/collapsible';
4
5export function Default() {
6 return (
7 <div className="space-y-3">
8 <Collapsible variant="bordered" defaultOpen>
9 <Collapsible.Trigger>
10 <span className="text-lg font-semibold">Prompt Engineering</span>
11 </Collapsible.Trigger>
12 <Collapsible.Content>
13 <div className="space-y-3 pt-3">
14 <p className="text-muted-foreground text-sm">
15 Prompt engineering is the practice of creating clear and effective instructions so an
16 AI model understands exactly what you want. Common techniques include:
17 </p>
18 <ul className="text-muted-foreground list-disc space-y-1 pl-5 text-sm">
19 <li>
20 <strong>Few-shot:</strong> Providing examples to guide the model.
21 </li>
22 <li>
23 <strong>Chain-of-thought:</strong> Asking the model to reason step by step.
24 </li>
25 <li>
26 <strong>System prompts:</strong> Defining the assistant's role and behavior.
27 </li>
28 <li>
29 <strong>Temperature:</strong> Adjusting creativity vs. precision.
30 </li>
31 </ul>
32 <div className="pt-2">
33 <code className="bg-secondary rounded px-2 py-1 text-xs">
34 You are an expert assistant specialized in...
35 </code>
36 </div>
37 </div>
38 </Collapsible.Content>
39 </Collapsible>
40 <Collapsible variant="bordered">
41 <Collapsible.Trigger>
42 <span className="text-lg font-semibold">RAG (Retrieval-Augmented Generation)</span>
43 </Collapsible.Trigger>
44 <Collapsible.Content>
45 <div className="space-y-3 pt-3">
46 <p className="text-muted-foreground text-sm">
47 RAG combines a language model with an external knowledge base. The AI retrieves
48 information from your documents and then generates an answer based on those sources.
49 </p>
50 <ul className="text-muted-foreground list-disc space-y-1 pl-5 text-sm">
51 <li>Reduces hallucinations by grounding answers in real data.</li>
52 <li>Allows up-to-date information without retraining models.</li>
53 <li>Perfect for documentation, support assistants, and intelligent search.</li>
54 </ul>
55 </div>
56 </Collapsible.Content>
57 </Collapsible>
58 <Collapsible variant="bordered">
59 <Collapsible.Trigger>
60 <span className="text-lg font-semibold">AI SDKs & Model Providers</span>
61 </Collapsible.Trigger>
62 <Collapsible.Content>
63 <div className="space-y-3 pt-3">
64 <p className="text-muted-foreground text-sm">
65 AI SDKs make it easy to connect your application with large language models. They
66 simplify authentication, requests, streaming, and model selection.
67 </p>
68 <ul className="text-muted-foreground list-disc space-y-1 pl-5 text-sm">
69 <li>
70 <strong>Groq:</strong> Extremely fast inference, ideal for real-time apps.
71 </li>
72 <li>
73 <strong>OpenAI:</strong> Access to GPT models, embeddings, and multimodal features.
74 </li>
75 <li>
76 <strong>Anthropic:</strong> Known for Claude models with strong reasoning and
77 safety.
78 </li>
79 <li>
80 <strong>Vercel AI SDK:</strong> A friendly layer for building chat and AI features
81 in React and Next.js.
82 </li>
83 </ul>
84 <p className="text-muted-foreground pt-2 text-xs">
85 *Quick note:* These SDKs help developers focus on the product experience instead of
86 handling low-level API details.
87 </p>
88 </div>
89 </Collapsible.Content>
90 </Collapsible>
91 </div>
92 );
93}

Installation

Copy and paste the following code into your project.
'use client';
import { ArrowDown01Icon } from '@hugeicons/core-free-icons';
import { HugeiconsIcon } from '@hugeicons/react';
import { cva } from 'class-variance-authority';
import { AnimatePresence, HTMLMotionProps, motion, useReducedMotion } from 'motion/react';
import * as React from 'react';
import { cn } from '../lib/cn';
// --- Animation constants (module level) ---
const COLLAPSIBLE_ICON_TRANSITION = { type: 'spring', stiffness: 300, damping: 20 } as const;
const COLLAPSIBLE_HEIGHT_VARIANTS = {
open: {
height: 'auto',
opacity: 1,
filter: 'blur(0px)',
transition: {
height: { duration: 0.3, ease: [0.04, 0.62, 0.23, 0.98] as [number, number, number, number] },
opacity: { duration: 0.25, delay: 0.05 },
filter: { duration: 0.3 },
},
},
closed: {
height: 0,
opacity: 0,
filter: 'blur(10px)',
transition: {
height: {
duration: 0.25,
ease: [0.04, 0.62, 0.23, 0.98] as [number, number, number, number],
},
opacity: { duration: 0.15 },
filter: { duration: 0.2 },
},
},
} as const;
const COLLAPSIBLE_INNER_VARIANTS = {
open: { y: 0, scale: 1, transition: { duration: 0.3, ease: 'easeOut' } },
closed: { y: -8, scale: 0.98, transition: { duration: 0.2 } },
} as const;
const COLLAPSIBLE_CONTENT_STYLE = { willChange: 'height, opacity, filter' } as const;
// --- CVA ---
const collapsibleVariants = cva('', {
variants: {
variant: {
default: '',
bordered: 'rounded-lg border border-border',
card: 'rounded-lg border border-border bg-card shadow-sm',
},
},
defaultVariants: {
variant: 'default',
},
});
const collapsibleTriggerVariants = cva(
[
'flex w-full items-center justify-between',
'transition-all duration-200',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
'disabled:pointer-events-none disabled:opacity-50',
].join(' '),
{
variants: {
variant: {
default: 'hover:opacity-80',
bordered: 'p-4',
card: 'p-4',
},
},
defaultVariants: {
variant: 'default',
},
},
);
const collapsibleContentVariants = cva('overflow-hidden', {
variants: {
variant: {
default: '',
bordered: 'px-4 pb-4',
card: 'px-4 pb-4',
},
},
defaultVariants: {
variant: 'default',
},
});
// --- Context ---
interface CollapsibleContextValue {
isOpen: boolean;
setIsOpen: (open: boolean) => void;
disabled?: boolean;
variant?: 'default' | 'bordered' | 'card';
id: string;
}
interface CollapsibleProps extends React.HTMLAttributes<HTMLDivElement> {
defaultOpen?: boolean;
open?: boolean;
onOpenChange?: (open: boolean) => void;
disabled?: boolean;
variant?: 'default' | 'bordered' | 'card';
}
interface CollapsibleTriggerProps extends Omit<HTMLMotionProps<'button'>, 'children'> {
showChevron?: boolean;
chevronIcon?: React.ReactNode;
chevronPosition?: 'left' | 'right';
asChild?: boolean;
children?: React.ReactNode;
}
interface CollapsibleContentProps extends Omit<HTMLMotionProps<'div'>, 'children'> {
forceMount?: boolean;
children?: React.ReactNode;
}
const CollapsibleContext = React.createContext<CollapsibleContextValue | null>(null);
const useCollapsibleContext = (): CollapsibleContextValue => {
const context = React.use(CollapsibleContext);
if (!context) {
throw new Error('Collapsible components must be used within Collapsible');
}
return context;
};
// --- Components ---
const CollapsibleRoot = ({
className,
defaultOpen = false,
open: controlledOpen,
onOpenChange,
disabled = false,
variant = 'default',
children,
ref,
...props
}: CollapsibleProps & { ref?: React.Ref<HTMLDivElement> }) => {
const [internalOpen, setInternalOpen] = React.useState(defaultOpen);
const isControlled = controlledOpen !== undefined;
const isOpen = isControlled ? controlledOpen : internalOpen;
const setIsOpen = React.useCallback(
(open: boolean) => {
if (disabled) return;
if (!isControlled) {
setInternalOpen(open);
}
onOpenChange?.(open);
},
[disabled, isControlled, onOpenChange],
);
const id = React.useId();
const contextValue = React.useMemo<CollapsibleContextValue>(
() => ({ isOpen, setIsOpen, disabled, variant, id }),
[isOpen, setIsOpen, disabled, variant, id],
);
return (
<CollapsibleContext value={contextValue}>
<div
ref={ref}
data-state={isOpen ? 'open' : 'closed'}
data-disabled={disabled ? '' : undefined}
className={cn(collapsibleVariants({ variant }), className)}
{...props}
>
{children}
</div>
</CollapsibleContext>
);
};
CollapsibleRoot.displayName = 'Collapsible';
const CollapsibleTrigger = ({
className,
showChevron = true,
chevronIcon,
chevronPosition = 'right',
asChild = false,
children,
onClick,
ref,
...props
}: CollapsibleTriggerProps & { ref?: React.Ref<HTMLButtonElement> }) => {
const { isOpen, setIsOpen, disabled, variant, id } = useCollapsibleContext();
const shouldReduceMotion = useReducedMotion();
const handleClick = React.useCallback(
(e: React.MouseEvent<HTMLButtonElement>) => {
if (!disabled) {
setIsOpen(!isOpen);
onClick?.(e);
}
},
[disabled, isOpen, setIsOpen, onClick],
);
const chevron = chevronIcon ?? (
<HugeiconsIcon icon={ArrowDown01Icon} className="h-4 w-4 shrink-0" size={16} />
);
if (asChild && React.isValidElement(children)) {
return React.cloneElement(children, {
onClick: handleClick,
'data-state': isOpen ? 'open' : 'closed',
'aria-expanded': isOpen,
'aria-controls': `${id}-content`,
disabled,
} as React.HTMLAttributes<HTMLElement>);
}
return (
<motion.button
ref={ref}
type="button"
onClick={handleClick}
disabled={disabled}
data-state={isOpen ? 'open' : 'closed'}
aria-expanded={isOpen}
aria-controls={`${id}-content`}
whileHover={
!disabled && !shouldReduceMotion
? { scale: 1.005, backgroundColor: 'rgba(0,0,0,0.02)' }
: {}
}
whileTap={!disabled && !shouldReduceMotion ? { scale: 0.99 } : {}}
className={cn(collapsibleTriggerVariants({ variant }), className)}
{...props}
>
{showChevron && chevronPosition === 'left' && (
<motion.span
aria-hidden="true"
animate={shouldReduceMotion ? undefined : { rotate: isOpen ? 90 : 0 }}
transition={COLLAPSIBLE_ICON_TRANSITION}
className="mr-2"
>
{chevron}
</motion.span>
)}
<span className="flex-1 text-left">{children}</span>
{showChevron && chevronPosition === 'right' && (
<motion.span
aria-hidden="true"
animate={shouldReduceMotion ? undefined : { rotate: isOpen ? 180 : 0 }}
transition={COLLAPSIBLE_ICON_TRANSITION}
className="ml-2"
>
{chevron}
</motion.span>
)}
</motion.button>
);
};
CollapsibleTrigger.displayName = 'CollapsibleTrigger';
const CollapsibleContent = ({
className,
forceMount = false,
children,
ref,
...props
}: CollapsibleContentProps & { ref?: React.Ref<HTMLDivElement> }) => {
const { isOpen, variant, id } = useCollapsibleContext();
return (
<AnimatePresence initial={false} mode="sync">
{(isOpen || forceMount) && (
<motion.div
ref={ref}
id={`${id}-content`}
variants={COLLAPSIBLE_HEIGHT_VARIANTS}
initial="closed"
animate="open"
exit="closed"
style={COLLAPSIBLE_CONTENT_STYLE}
className={cn(collapsibleContentVariants({ variant }), className)}
{...props}
>
<motion.div
variants={COLLAPSIBLE_INNER_VARIANTS}
initial="closed"
animate="open"
exit="closed"
>
{children}
</motion.div>
</motion.div>
)}
</AnimatePresence>
);
};
CollapsibleContent.displayName = 'CollapsibleContent';
const Collapsible = Object.assign(CollapsibleRoot, {
Trigger: CollapsibleTrigger,
Content: CollapsibleContent,
});
export { Collapsible, collapsibleContentVariants, collapsibleTriggerVariants, collapsibleVariants };
export type { CollapsibleContentProps, CollapsibleProps, CollapsibleTriggerProps };
Make sure to update the import paths to match your project structure.

Anatomy

import { Collapsible } from '@/components/ui/collapsible';
<Collapsible>
<Collapsible.Trigger>Can I use this in my project?</Collapsible.Trigger>
<Collapsible.Content>Yes. Free to use for personal and commercial projects.</Collapsible.Content>
</Collapsible>

Examples

Bordered

Collapsible with border and rounded corners.

React is a JavaScript library for building user interfaces. It lets you create reusable components that manage their own state.

1'use client';
2
3import { Collapsible } from '@/components/ui/collapsible';
4
5export function Bordered() {
6 return (
7 <div className="space-y-3">
8 <Collapsible variant="bordered" defaultOpen>
9 <Collapsible.Trigger>
10 <span className="text-lg font-semibold">What is React?</span>
11 </Collapsible.Trigger>
12 <Collapsible.Content>
13 <div className="space-y-2 pt-3">
14 <p className="text-muted-foreground text-sm">
15 React is a JavaScript library for building user interfaces. It lets you create
16 reusable components that manage their own state.
17 </p>
18 </div>
19 </Collapsible.Content>
20 </Collapsible>
21
22 <Collapsible variant="bordered">
23 <Collapsible.Trigger>
24 <span className="text-lg font-semibold">What are React Hooks?</span>
25 </Collapsible.Trigger>
26 <Collapsible.Content>
27 <div className="space-y-2 pt-3">
28 <p className="text-muted-foreground text-sm">
29 Hooks are functions that let you use state and other React features in functional
30 components. Common hooks include useState, useEffect, and useContext.
31 </p>
32 </div>
33 </Collapsible.Content>
34 </Collapsible>
35
36 <Collapsible variant="bordered">
37 <Collapsible.Trigger>
38 <span className="text-lg font-semibold">What is JSX?</span>
39 </Collapsible.Trigger>
40 <Collapsible.Content>
41 <div className="space-y-2 pt-3">
42 <p className="text-muted-foreground text-sm">
43 JSX is a syntax extension for JavaScript that looks similar to HTML. It allows you to
44 write UI components in a more declarative way.
45 </p>
46 </div>
47 </Collapsible.Content>
48 </Collapsible>
49 </div>
50 );
51}

Card

Collapsible with card styling including border, background, and shadow.

Welcome to our platform! Here's everything you need to know to get started with your first project.

  • Create your account
  • Set up your workspace
  • Invite team members
  • Start building!
1'use client';
2
3import { Collapsible } from '@/components/ui/collapsible';
4
5export function Card() {
6 return (
7 <div className="space-y-3">
8 <Collapsible variant="card" defaultOpen>
9 <Collapsible.Trigger>
10 <span className="text-lg font-semibold">🚀 Getting Started</span>
11 </Collapsible.Trigger>
12 <Collapsible.Content>
13 <div className="space-y-2 pt-3">
14 <p className="text-muted-foreground text-sm">
15 Welcome to our platform! Here's everything you need to know to get started with your
16 first project.
17 </p>
18 <ul className="text-muted-foreground list-disc space-y-1 pl-5 text-sm">
19 <li>Create your account</li>
20 <li>Set up your workspace</li>
21 <li>Invite team members</li>
22 <li>Start building!</li>
23 </ul>
24 </div>
25 </Collapsible.Content>
26 </Collapsible>
27
28 <Collapsible variant="card">
29 <Collapsible.Trigger>
30 <span className="text-lg font-semibold">📚 Documentation</span>
31 </Collapsible.Trigger>
32 <Collapsible.Content>
33 <div className="space-y-2 pt-3">
34 <p className="text-muted-foreground text-sm">
35 Explore our comprehensive documentation to learn about all features and capabilities.
36 </p>
37 <div className="flex flex-wrap gap-2 pt-2">
38 <code className="bg-secondary rounded px-2 py-1 text-xs">API Reference</code>
39 <code className="bg-secondary rounded px-2 py-1 text-xs">Guides</code>
40 <code className="bg-secondary rounded px-2 py-1 text-xs">Examples</code>
41 </div>
42 </div>
43 </Collapsible.Content>
44 </Collapsible>
45
46 <Collapsible variant="card">
47 <Collapsible.Trigger>
48 <span className="text-lg font-semibold">⚙️ Settings</span>
49 </Collapsible.Trigger>
50 <Collapsible.Content>
51 <div className="space-y-2 pt-3">
52 <p className="text-muted-foreground text-sm">
53 Customize your experience with our flexible settings and preferences.
54 </p>
55 </div>
56 </Collapsible.Content>
57 </Collapsible>
58 </div>
59 );
60}

API Reference

Collapsible

PropTypeDefaultDescription
defaultOpenbooleanfalseInitial open state (uncontrolled mode).
openbooleanControlled open state.
onOpenChange(open: boolean) => voidCallback fired when the open state changes.
disabledbooleanfalseDisables interaction.
variant'default' | 'bordered' | 'card''default'Visual style of the collapsible container.
classNamestringAdditional classes.
childrenReact.ReactNodeContent inside the collapsible.

CollapsibleTrigger

PropTypeDefaultDescription
showChevronbooleantrueShows the chevron icon.
chevronIconReact.ReactNodeCustom icon for the chevron.
chevronPosition'left' | 'right''right'Position of the chevron icon.
asChildbooleanfalseAllows passing a custom child element as the trigger.
childrenReact.ReactNodeTrigger label or content.
onClick(event: React.MouseEvent) => voidClick handler (merged with open/close logic).
classNamestringAdditional classes.

CollapsibleContent

PropTypeDefaultDescription
forceMountbooleanfalseRenders content even when closed (useful for SSR).
childrenReact.ReactNodeContent to be revealed/collapsed.
classNamestringAdditional classes.