Skip to contentVibraUI
Feedback & status

Live indicator

A small pill that says a panel is streaming, with a pulsing dot while it is.

Server-compatible: no hooks, no client boundary. It is a status region carrying visually hidden "updating" or "paused" text, so the switch is announced and never depends on the dot alone. The pulse is dropped for readers who prefer reduced motion.

Install

npx shadcn@latest add @vibra/live-indicator

Needs the @vibra registry in your components.json — set it up once.

Examples

Props

PropTypeDefaultDescription
labelReact.ReactNodeLiveKeep it to one short word so the pill stays a pill.
activebooleantrueFalse parks the badge: the dot goes grey and stops pulsing.

Dependencies

Registry

Source

components/ui/live-indicator.tsx
import * as React from "react"

import { cn } from "@/lib/utils"

export type LiveIndicatorProps = React.ComponentProps<"span"> & {
  /** Replaces the word "Live" — keep it to one short word so the pill stays a pill. */
  label?: React.ReactNode
  /** False parks the badge: the dot goes grey and stops pulsing. */
  active?: boolean
}

/**
 * A pill that says a panel is streaming. It is a live region, so a screen
 * reader hears the switch between streaming and parked as it happens.
 */
function LiveIndicator({ className, label = "Live", active = true, ...props }: LiveIndicatorProps) {
  return (
    <span
      data-slot="live-indicator"
      data-active={active || undefined}
      role="status"
      className={cn(
        "inline-flex w-fit items-center gap-1.5 rounded-full border border-border bg-surface py-0.5 pe-2.5 ps-2",
        className
      )}
      {...props}
    >
      <span className="relative flex size-1.5 shrink-0" aria-hidden="true">
        {active ? (
          <span
            data-slot="live-indicator-pulse"
            className="absolute inline-flex size-full animate-ping rounded-full bg-danger opacity-60 motion-reduce:hidden in-data-[motion=reduced]:hidden"
          />
        ) : null}
        <span
          data-slot="live-indicator-dot"
          className={cn(
            "relative inline-flex size-full rounded-full",
            active ? "bg-danger" : "bg-muted-foreground"
          )}
        />
      </span>
      <span
        data-slot="live-indicator-label"
        className={cn(
          "text-2xs font-medium tracking-wide uppercase",
          active ? "text-foreground" : "text-muted-foreground"
        )}
      >
        {label}
      </span>
      <span className="sr-only">{active ? "updating" : "paused"}</span>
    </span>
  )
}

export { LiveIndicator }