shadimage
openstatus
blockstatus-component-groupsource: ok · refreshed

Status Component Group

Collapsible group wrapper for status components

npx shadcn add "https://openstatus.dev/r/status-component-group.json"
free

Where it goes

stats

Interaction

animated-counteraccordion

Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light

Registry dependencies

https://openstatus.dev/r/status-component.jsonhttps://openstatus.dev/r/status-types.jsoncollapsible

Files1 · source cached 10/8/2026, 7:40:26 AM

src/components/blocks/status-component-group.tsx
"use client";

import {
  StatusComponentIcon,
  StatusComponentStatus,
} from "@/components/blocks/status-component";
import type { StatusType } from "@/components/blocks/status.types";
import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
} from "@/components/ui/collapsible";
import { cn } from "@/lib/utils";
import { useEffect, useState } from "react";

/**
 * StatusComponentGroup - Collapsible group for organizing related monitors
 *
 * A collapsible container component for grouping related StatusComponent items,
 * displaying an aggregate status for the group. The component shows:
 * - Group title
 * - Aggregate status label (e.g., "Operational", "Degraded")
 * - Status icon with appropriate color
 * - Expandable/collapsible content area for grouped monitors
 *
 * **Key Features**:
 * - **Collapsible**: Click to expand/collapse grouped monitors
 * - **Hydration-Aware Animations**: Prevents layout shift on initial render by delaying
 *   animations until after mount (avoids animation flash when defaultOpen=true)
 * - **Aggregate Status**: Shows overall status for the entire group
 * - **Keyboard Accessible**: Full keyboard support via Radix UI Collapsible
 * - **Hover Effects**: Subtle border and background changes on hover/open
 *
 * The component uses a hydration-aware animation pattern: animations are disabled
 * on the server and first client render, then enabled after mount. This prevents
 * layout shifts when `defaultOpen={true}` is used.
 *
 * @param title - The group name/title
 * @param status - The aggregate status type for the group (controls icon color and status label)
 * @param defaultOpen - Whether the group starts expanded (default: false)
 * @param children - StatusComponent items or other content to display when expanded
 *
 * @example
 * // Basic collapsible group with multiple monitors
 * ```tsx
 * <StatusComponentGroup
 *   title="API Services"
 *   status="success"
 *   defaultOpen={false}
 * >
 *   <StatusComponent variant="success">
 *     <StatusComponentHeader>
 *       <StatusComponentHeaderLeft>
 *         <StatusComponentIcon />
 *         <StatusComponentTitle>REST API</StatusComponentTitle>
 *       </StatusComponentHeaderLeft>
 *     </StatusComponentHeader>
 *   </StatusComponent>
 *   <StatusComponent variant="success">
 *     <StatusComponentHeader>
 *       <StatusComponentHeaderLeft>
 *         <StatusComponentIcon />
 *         <StatusComponentTitle>GraphQL API</StatusComponentTitle>
 *       </StatusComponentHeaderLeft>
 *     </StatusComponentHeader>
 *   </StatusComponent>
 * </StatusComponentGroup>
 * ```
 *
 * @example
 * // Degraded group with mixed statuses
 * ```tsx
 * <StatusComponentGroup
 *   title="Database Cluster"
 *   status="degraded"
 *   defaultOpen={true}
 * >
 *   <StatusComponent variant="success">
 *     <StatusComponentHeader>
 *       <StatusComponentHeaderLeft>
 *         <StatusComponentIcon />
 *         <StatusComponentTitle>Primary DB</StatusComponentTitle>
 *       </StatusComponentHeaderLeft>
 *     </StatusComponentHeader>
 *   </StatusComponent>
 *   <StatusComponent variant="degraded">
 *     <StatusComponentHeader>
 *       <StatusComponentHeaderLeft>
 *         <StatusComponentIcon />
 *         <StatusComponentTitle>Replica DB</StatusComponentTitle>
 *       </StatusComponentHeaderLeft>
 *     </StatusComponentHeader>
 *   </StatusComponent>
 * </StatusComponentGroup>
 * ```
 *
 * @example
 * // Multiple groups for different service categories
 * ```tsx
 * <StatusContent>
 *   <StatusComponentGroup title="Core Services" status="success">
 *     // Core service monitors...
 *   </StatusComponentGroup>
 *   <StatusComponentGroup title="Third-Party APIs" status="degraded" defaultOpen>
 *     // Third-party monitors...
 *   </StatusComponentGroup>
 *   <StatusComponentGroup title="Internal Tools" status="success">
 *     // Internal tool monitors...
 *   </StatusComponentGroup>
 * </StatusContent>
 * ```
 *
 * @see StatusComponent - For individual monitor displays within the group
 * @see StatusComponentIcon - For the status icon displayed in the header
 * @see StatusComponentStatus - For the status label displayed in the header
 */
export function StatusComponentGroup({
  children,
  title,
  status,
  className,
  defaultOpen = false,
  ...props
}: React.ComponentProps<"div"> & {
  title: string;
  status?: Exclude<StatusType, "empty">;
  children?: React.ReactNode;
  defaultOpen?: boolean;
}) {
  const [mounted, setMounted] = useState(false);

  useEffect(() => {
    setMounted(true);
  }, []);

  return (
    <div {...props}>
      <Collapsible
        defaultOpen={defaultOpen}
        data-slot="status-component-group"
        className={cn(
          "-mx-3",
          "bg-muted/50 hover:border-border/50 data-[state=open]:border-border/50 data-[state=open]:bg-muted/50 rounded-lg border border-transparent",
          className,
        )}
      >
        <CollapsibleTrigger
          data-slot="status-component-group-trigger"
          className={cn(
            "group/component flex w-full items-center justify-between gap-2 rounded-lg px-3 py-2 font-mono font-medium",
            "cursor-pointer",
            className,
          )}
          data-variant={status}
          aria-label={`Toggle ${title} status details`}
        >
          {title}
          <div className="flex items-center gap-2">
            <StatusComponentStatus className="text-sm" />
            <StatusComponentIcon />
          </div>
        </CollapsibleTrigger>
        <CollapsibleContent
          data-slot="status-component-group-content"
          data-animate={mounted}
          className={cn(
            "border-border/50 flex flex-col gap-3 border-t px-3 py-2",
            "overflow-hidden",
            // REMINDER: otherwise, if defaultOpen is true, the animation will be triggered and we have a layout shift
            "data-[animate=true]:data-[state=closed]:animate-collapsible-up data-[animate=true]:data-[state=open]:animate-collapsible-down",
          )}
        >
          {children}
        </CollapsibleContent>
      </Collapsible>
    </div>
  );
}
StatusComponentGroup.displayName = "StatusComponentGroup";

More from openstatus

Browse all components in this library →