Provider status

Pro

A status card for the AI providers your product depends on. Each row shows the provider's real mark, its health as an icon and label, p50 latency and a 30-day uptime bar with a tooltip for each day. An incident row expands into its timeline of updates, and the card checks again on a visible countdown.

Live · keyboard ready

AI providers

1 outage · 1 degraded · 4 operational
Refresh in 8s
  • AnthropicClaude Opus 5.5, Sonnet 5.5Operational412 ms
    99.96%
  • OpenAIGPT-6.1 Sol, GPT-6 LunaOutage2.14 s
    99.61%
    Elevated error rates on GPT-6.1 SolStarted 14:02 UTC
    1. Investigating14:02 UTCWe are seeing elevated 5xx errors on the Responses API for GPT-6.1 Sol. GPT-6 Luna is not affected.
  • Google GeminiGemini 4 Argon, 3.8 FlashDegraded1.38 s
    99.93%
  • Mistral AIMistral Large 4Operational367 ms
    99.81%
  • xAIGrok 4.7Operational448 ms
    99.98%
  • Amazon BedrockClaude Sonnet 5.5 in us-east-1Operational543 ms
    99.97%

Sample data. Latency is the median over the last 5 minutes; uptime counts degraded time at 30%.

  • Showing the live health, latency and recent uptime of the model providers your product routes to.
  • Explaining degraded AI features to users or admins with the provider's incident updates in place.
  • A routing or fallback settings page where people need to see which providers are healthy right now.
  • A full public status page with components, subscriptions and history: build a page, not a card.
  • A single provider's state inside a chat header: use agent-status or a small badge.
  • Detailed latency charts over time: use a chart component.

Installation

Pro source and install commands unlock with a Pro plan.

example.tsx
import { ProviderStatus } from "@/components/arc/provider-status/provider-status";import { AnthropicMark, OpenaiMark } from "@/components/arc/provider-status/provider-status"; export function Health() {  const [providers, setProviders] = useState<ProviderStatusItem[]>(initial);  return (    <ProviderStatus      providers={providers}      refreshInterval={60_000}      onRefresh={async signal => {        const res = await fetch("/api/provider-status", { signal });        if (!res.ok) throw new Error("Couldn’t reach the status feeds. Showing the last results.");        return (await res.json()) as ProviderStatusItem[];      }}      onOpenIncident={(provider, incident) => window.open(incident.url, "_blank")}      footnote="Latency is the median over the last 5 minutes."    />  );}

API reference

3 parts. The first is the root.

ProviderStatus

A status card for the AI providers a product depends on. Each row shows the provider's real mark, its health as an icon and label (Operational, Degraded, Outage), median latency in tabular numerals and a 30 day uptime bar of small ticks with a tooltip per day. A row with an incident expands into its updates (Investigating, Identified, Monitoring, Resolved), newest first, and new updates grow the panel smoothly. The header sums up health across providers and checks again on a visible countdown ring, with a refresh button, a Checking state and an error state that keeps the last results. Narrow cards switch to two lines per row. With no props it plays a sample story on simulated checks: an OpenAI outage and a Gemini slowdown resolve, Mistral has a short incident, and one check fails to show the error state.

PropTypeDefaultDescription
providersProviderStatusItem[]–The providers to show. Each has id, name, mark (a 16px ReactNode), detail (a short line such as the models you call), status ("operational" | "degraded" | "outage"), latencyMs, days and incident. Leave it out to play the built in sample.
providers[].daysProviderDay[]–Daily history, oldest first; the bar shows the last 30 and pads missing days as No data. Each day has date ("YYYY-MM-DD"), uptime (0 to 100, or null), an optional status (derived from uptime when left out: below 99.9 degraded, below 99 outage) and an optional note for the tooltip. The last day is labeled Today.
providers[].incident{ id: string; title: string; updates: IncidentUpdate[]; url?: string }–An open or just resolved incident. Its row becomes a button that expands the updates. Each update has stage ("investigating" | "identified" | "monitoring" | "resolved"), at (a Date, a timestamp shown as HH:MM UTC, or a preformatted string) and message.
onRefresh(signal: AbortSignal) => Promise<ProviderStatusItem[] | void>–Fetches fresh status on the countdown and on the refresh button. Return the new providers, or update the providers prop yourself and return nothing. Throw to show the error state; the last results stay on screen and the countdown turns into a retry. A newer check aborts the older one through the signal. Without it the refresh controls are hidden.
refreshIntervalnumber | null30000Milliseconds between automatic checks. The countdown pauses while the card is off screen or the tab is hidden. Retries after an error wait at most 10 seconds. Null turns auto refresh off and keeps the refresh button. The sample uses 8000.
loadingbooleanfalseShows skeleton rows with the same row height, for the first load.
errorstring | null–An error to show in the header, such as a failed first load. Errors thrown by onRefresh show on their own.
titlestring"AI providers"Card title.
expandedIdstring | null–Which provider's incident is open. Pair with onExpandedChange.
defaultExpandedIdstring | nullnullThe incident open on first render when uncontrolled.
onExpandedChange(id: string | null) => void–Called when an incident row is opened or closed.
onOpenIncident(provider: ProviderStatusItem, incident: ProviderIncident) => void–Called by the Status page link in an open incident. Without it, the link opens incident.url in a new tab; with neither, the link is hidden.
footnoteReactNode–A quiet line under the list, such as how latency and uptime are measured. The sample shows a sample data note.
noticestring–A one-off message for screen readers, such as the result of an action.
classNamestring–Class on the root element.

sampleProviders

The sample data behind the preview, exported for prototypes and tests: sampleProviders(check) returns six providers (Anthropic, OpenAI, Google Gemini, Mistral AI, xAI, Amazon Bedrock) after a number of simulated checks. Every number is a plausible sample, not a measurement.

PropTypeDefaultDescription
checknumber–How many simulated checks have run. The story loops every 11 checks.

AnthropicMark, OpenaiMark, GeminiColorMark, MistralColorMark, XaiMark, AwsMark

The provider marks used by the sample, from Simple Icons (CC0) and Lobe Icons (MIT). Mono marks draw with currentColor; Gemini and Mistral keep their brand fills.

PropTypeDefaultDescription
titlestring–Accessible name of the mark.
Tab
Moves to the refresh button, each incident row and each uptime bar.
EnterorSpace
On an incident row, opens or closes its updates.
Arrow LeftorArrow Right
On an uptime bar, moves the day tooltip one day back or forward.
HomeorEnd
On an uptime bar, jumps to the oldest day or today.
Escape
On an uptime bar, hides the day tooltip.
  • The card is a labelled section. Rows are a list; an incident row has a full-row button with aria-expanded and aria-controls, named with the provider, its health and the incident title.
  • Health is never color alone: every status shows an icon and a label, and day ticks carry their health in the tooltip and in the bar's label.
  • Each uptime bar is one tab stop with a label that sums up the 30 days. Arrow keys move through the days and a polite live region reads each one, so 180 ticks never flood the tab order.
  • A polite live region announces checks, refresh errors and simulated actions ("Simulated, no requests were sent").
  • Focus rings come from the shared :focus-visible styles; the row button's ring sits just inside the row.
  • Health labels, latency, uptime and the summary change with a single crossfade (out in 100ms, in over 160ms with a 2px rise) inside a reserved grid cell, so nothing reflows.
  • The incident panel springs to its measured height with no bounce (visual duration 240ms); new updates fade in while the panel grows.
  • Ticks change color over 240ms. The countdown ring is a linear CSS drain that pauses with the countdown off screen; Checking swaps it for a short spinner and a shimmer label.
  • Reduced motion keeps every end state and drops the rise, the height spring, the spinner and the skeleton pulse.
  • From 560px wide each row is one line with columns for status, p50 latency, the uptime bar and the 30 day percentage.
  • Below 560px (a container query, so it follows the card, not the window) rows take two lines: name and status above, a full width uptime bar and percentage below, with latency under the name.
  • Holds up at 360px with no horizontal scroll; long names and details truncate with an ellipsis.
  • Touch: tapping a tick shows its day, dragging sideways scrubs, and vertical scrolling still works.
  • One shared tooltip for the whole card, positioned on demand, instead of one per tick.
  • The countdown re-renders once per second at most; the ring animates in CSS. Both pause off screen and in background tabs.
  • Refreshes are abortable and never overlap.

Notes for AI

Give your coding assistant the Markdown reference instead of screenshots.

  • Use it for a provider health view in an AI product: a settings page, an admin dashboard tile or a status popover. Wire onRefresh to your own aggregation endpoint; do not poll provider status pages from the browser.
  • Pass real marks at 16px. Mono marks should use currentColor so they follow the theme.
  • Keep latency and uptime as plain numbers; the component formats them (412 ms, 1.92 s, 99.61%).
  • Label sample or synthetic numbers with the footnote prop.

The full library index for assistants is at /llms.txt.