Bloom Logo

Collapsible

An interactive component that expands and collapses content panels with smooth CSS Grid auto-height transitions and optional lazy content rendering.

importimport { Collapsible } from "@/components/ui/collapsible/collapsible";
$npx @bloomui-react/cli add collapsible
collapsible.tsx
"use client";

import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
import * as React from "react";
import { cn } from "@/lib/utils";

const Collapsible = CollapsiblePrimitive.Root;

const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;

export interface CollapsibleContentProps
  extends React.ComponentPropsWithoutRef<
    typeof CollapsiblePrimitive.CollapsibleContent
  > {
  lazy?: boolean;
}

const CollapsibleContent = React.forwardRef<
  React.ElementRef<typeof CollapsiblePrimitive.CollapsibleContent>,
  CollapsibleContentProps
>(({ className, children, lazy = false, ...props }, ref) => {
  const [hasBeenOpened, setHasBeenOpened] = React.useState(false);
  const contentRef = React.useRef<HTMLDivElement>(null);

  React.useImperativeHandle(ref, () => contentRef.current as HTMLDivElement);

  React.useEffect(() => {
    if (!lazy) return;
    const el = contentRef.current;
    if (!el) return;

    const checkState = () => {
      const state = el.getAttribute("data-state");
      if (state === "open") {
        setHasBeenOpened(true);
      }
    };

    checkState();

    const observer = new MutationObserver((mutations) => {
      mutations.forEach((mutation) => {
        if (
          mutation.type === "attributes" &&
          mutation.attributeName === "data-state"
        ) {
          checkState();
        }
      });
    });

    observer.observe(el, { attributes: true });
    return () => observer.disconnect();
  }, [lazy]);

  const shouldRenderChildren = !lazy || hasBeenOpened;

  return (
    <CollapsiblePrimitive.CollapsibleContent
      ref={contentRef}
      className={cn(
        "overflow-hidden transition-all data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down",
        className,
      )}
      {...props}
    >
      {shouldRenderChildren ? children : null}
    </CollapsiblePrimitive.CollapsibleContent>
  );
});
CollapsibleContent.displayName =
  CollapsiblePrimitive.CollapsibleContent.displayName;

export { Collapsible, CollapsibleContent, CollapsibleTrigger };

Default

Standard collapsible panel with a trigger button that reveals hidden items using a smooth height animation.

Lazy Content Rendering

Defers rendering of child components until the panel is expanded for the first time, optimizing initial load performance.

lazy: boolean

Default Open

Pre-expanded collapsible panel using controlled state initialized as open.

open: booleandefaultOpen: boolean
NotificationsEnabled
Two-Factor AuthenticationPending
API AccessRestricted

Card Style

A collapsible section styled as a bordered card container with icon, heading, and description in the trigger.

open: anyisOpen: anyonOpenChange: anysetIsOpen: anyrounded-2xl: anyborder: anyborder-zinc-200: anydark: anyborder-zinc-800: anybg-white: anybg-zinc-900: anyshadow-xs: anyoverflow-hidden: anyasChild: anyw-full: anyflex: anyitems-center: anyjustify-between: anygap-4: anypx-5: anypy-4: anycursor-pointer: anygap-3: anyicon: anyhugeicons: anyshield-01: anytext-sm: anyfont-semibold: anytext-xs: anytext-zinc-500: anyarrow-down-01: anyborder-t: anyspace-y-3: anycolor: anysuccess: any

Disabled State

Disables user interaction with the collapsible trigger, preventing expand or collapse.

disabled: boolean
API Reference

Props — Collapsible & CollapsibleContent

Properties for configuring the Collapsible and CollapsibleContent components.

PropTypeDefaultDescription
openbooleanControlled open state of the collapsible panel.
defaultOpenbooleanfalseInitial open state when using uncontrolled mode.
onOpenChange(open: boolean) => voidCallback function fired when the open state changes.
disabledbooleanfalseDisables interaction with the collapsible trigger.
lazybooleanfalseDefers rendering child DOM elements until expanded for the first time.