Status Badge
Pulsing status pills for operational, degraded, outage and maintenance.
All systems operationalDegraded performanceMajor outageScheduled maintenance
components/ui/status-badge.tsx
"use client";
import { motion } from "motion/react";
type Status = "online" | "degraded" | "down" | "maintenance";
const styles: Record<Status, { label: string; dot: string; badge: string }> = {
online: { label: "All systems operational", dot: "bg-emerald-500", badge: "border-emerald-200 bg-emerald-50 text-emerald-700" },
degraded: { label: "Degraded performance", dot: "bg-amber-500", badge: "border-amber-200 bg-amber-50 text-amber-700" },
down: { label: "Major outage", dot: "bg-rose-500", badge: "border-rose-200 bg-rose-50 text-rose-700" },
maintenance: { label: "Scheduled maintenance", dot: "bg-blue-500", badge: "border-blue-200 bg-blue-50 text-blue-700" },
};
export function StatusBadge({ status, label }: { status: Status; label?: string }) {
const s = styles[status];
return (
<span role="status" className={`inline-flex items-center gap-2 rounded-full border px-3 py-1 text-xs font-medium ${s.badge}`}>
<span className="relative flex size-2">
{status !== "maintenance" && (
<motion.span
className={`absolute inline-flex size-full rounded-full ${s.dot}`}
animate={{ scale: [1, 2.4], opacity: [0.6, 0] }}
transition={{ duration: status === "down" ? 0.9 : 1.6, repeat: Infinity, ease: "easeOut" }}
/>
)}
<span className={`relative inline-flex size-2 rounded-full ${s.dot}`} />
</span>
{label ?? s.label}
</span>
);
}
export default function Demo() {
return (
<div className="flex flex-col items-center gap-3">
{(Object.keys(styles) as Status[]).map((s) => (
<StatusBadge key={s} status={s} />
))}
</div>
);
}
Installation
Install the dependencies
$ npm i motion lucide-reactCopy the source into
components/ui/status-badge.tsxcomponents/ui/status-badge.tsx"use client"; import { motion } from "motion/react"; type Status = "online" | "degraded" | "down" | "maintenance"; const styles: Record<Status, { label: string; dot: string; badge: string }> = { online: { label: "All systems operational", dot: "bg-emerald-500", badge: "border-emerald-200 bg-emerald-50 text-emerald-700" }, degraded: { label: "Degraded performance", dot: "bg-amber-500", badge: "border-amber-200 bg-amber-50 text-amber-700" }, down: { label: "Major outage", dot: "bg-rose-500", badge: "border-rose-200 bg-rose-50 text-rose-700" }, maintenance: { label: "Scheduled maintenance", dot: "bg-blue-500", badge: "border-blue-200 bg-blue-50 text-blue-700" }, }; export function StatusBadge({ status, label }: { status: Status; label?: string }) { const s = styles[status]; return ( <span role="status" className={`inline-flex items-center gap-2 rounded-full border px-3 py-1 text-xs font-medium ${s.badge}`}> <span className="relative flex size-2"> {status !== "maintenance" && ( <motion.span className={`absolute inline-flex size-full rounded-full ${s.dot}`} animate={{ scale: [1, 2.4], opacity: [0.6, 0] }} transition={{ duration: status === "down" ? 0.9 : 1.6, repeat: Infinity, ease: "easeOut" }} /> )} <span className={`relative inline-flex size-2 rounded-full ${s.dot}`} /> </span> {label ?? s.label} </span> ); } export default function Demo() { return ( <div className="flex flex-col items-center gap-3"> {(Object.keys(styles) as Status[]).map((s) => ( <StatusBadge key={s} status={s} /> ))} </div> ); }Use it
The file exports the component and a
Demoshowing typical usage. Import whichever you need.