# Uptime

A strip of daily bars showing a service's status, day by day.

Source: [uptime.tsx](https://github.com/jakejarvis/jarvis-ui/blob/main/src/components/ui/uptime.tsx)

```tsx
import { Card, CardContent } from "@/components/ui/card";
import {
  Uptime,
  UptimeFooter,
  UptimeLabel,
  UptimeStatus,
  UptimeTrack,
} from "@/components/ui/uptime";
import { uptime } from "@/examples/data";

const services = [
  {
    name: "API",
    days: uptime({
      12: { downtime: 24, incidents: [{ title: "Elevated error rates", minutes: 24 }] },
      47: { degraded: 95, incidents: [{ title: "Slow responses in us-east", minutes: 95 }] },
    }),
  },
  {
    name: "Dashboard",
    days: uptime({ 63: { degraded: 18 } }),
  },
  {
    name: "Webhooks",
    days: uptime({
      0: { degraded: 42, incidents: [{ title: "Delayed deliveries", minutes: 42 }] },
      21: { downtime: 7 },
      22: { degraded: 130 },
    }),
  },
];

export default function UptimeStatusPage() {
  return (
    <Card className="w-full">
      <CardContent className="flex flex-col gap-6">
        {services.map((service) => (
          <Uptime key={service.name} data={service.days} label={service.name}>
            <UptimeLabel>{service.name}</UptimeLabel>
            <UptimeStatus />
            <UptimeTrack />
            <UptimeFooter />
          </Uptime>
        ))}
      </CardContent>
    </Card>
  );
}
```

## Installation

Add the registry, once per project:

```bash
npx shadcn@latest registry add @jarvis=https://ui.jarv.is/r/{name}.json
```

Then add the component:

```bash
npx shadcn@latest add @jarvis/uptime
```

## Usage

```tsx
import {
  Uptime,
  UptimeFooter,
  UptimeLabel,
  UptimeStatus,
  UptimeTrack,
} from "@/components/ui/uptime";

const days = [
  { date: "2026-09-28" },
  { date: "2026-09-29", downtime: 12 },
  { date: "2026-09-30", degraded: 40, incidents: [{ title: "Slow responses", minutes: 40 }] },
];

export function ApiUptime() {
  return (
    <Uptime data={days} label="API">
      <UptimeLabel>API</UptimeLabel>
      <UptimeStatus />
      <UptimeTrack />
      <UptimeFooter />
    </Uptime>
  );
}
```

`data` holds a day per entry, oldest first, and the last is today. A day with `downtime` minutes is
an outage, and one with `degraded` minutes is degraded. Only downtime counts against uptime, which
rounds down, so a day with any downtime never shows 100%. A day marked `down` with neither
`downtime` nor `uptime` shows no percentage and is left out of the total. Days before the first in
`data` show as no data, so strips for a newer service still line up with the rest.

Point at a bar, or focus the strip and move with the arrow keys, to see its day, uptime and
incidents. On a touch screen, tap it.

The strip shows every day in `data`, or the last `days` of them. Where there's no room for each bar
to be `minBarWidth` wide, it shows as many of the latest days as fit, and the footer counts them.
The uptime is still worked out over every day, so it reads the same at any width.

## Composition

- `Uptime`
  - `UptimeLabel`
  - `UptimeStatus`
  - `UptimeTrack`
  - `UptimeFooter`

## Examples

### Current status

`UptimeStatus` shows the last day's status. Set `current` on `Uptime` while an incident is
underway, and give the status your own words.

```tsx
import {
  Uptime,
  UptimeFooter,
  UptimeLabel,
  UptimeStatus,
  UptimeTrack,
} from "@/components/ui/uptime";
import { uptime } from "@/examples/data";

const days = uptime({
  0: { downtime: 18, incidents: [{ title: "Payments failing for some cards", minutes: 18 }] },
  30: { degraded: 15 },
});

export default function UptimeCurrentStatus() {
  return (
    <Uptime data={days} current="down" label="Checkout" className="max-w-xl">
      <UptimeLabel>Checkout</UptimeLabel>
      <UptimeStatus>Investigating</UptimeStatus>
      <UptimeTrack />
      <UptimeFooter />
    </Uptime>
  );
}
```

### Compact

`UptimeTrack` works alone. Here each root takes `contents`, so its label and track sit in the
grid's columns, and `days={30}` gives the newest region's strip the same 30 days as the others.

```tsx
import { Uptime, UptimeTrack } from "@/components/ui/uptime";
import { uptime } from "@/examples/data";

const regions = [
  { name: "us-east-1", days: uptime({ 4: { downtime: 12 } }, "2026-09-30", 30) },
  { name: "eu-west-1", days: uptime({ 17: { degraded: 40 } }, "2026-09-30", 30) },
  { name: "ap-south-1", days: uptime({}, "2026-09-30", 18) },
];

export default function UptimeCompact() {
  return (
    <div className="grid w-full max-w-md grid-cols-[auto_minmax(0,1fr)] items-center gap-x-4 gap-y-3">
      {regions.map((region) => (
        <Uptime
          key={region.name}
          data={region.days}
          days={30}
          label={region.name}
          className="contents"
        >
          <span className="font-mono text-2xs text-muted-foreground">{region.name}</span>
          <UptimeTrack className="h-5" />
        </Uptime>
      ))}
    </div>
  );
}
```

### Labels

`labels` replaces the words the strip shows and reads out, and `locale` formats its dates and
percentages. Pass only the ones to change.

```tsx
import {
  Uptime,
  UptimeFooter,
  UptimeLabel,
  UptimeStatus,
  UptimeTrack,
} from "@/components/ui/uptime";
import { uptime } from "@/examples/data";

const days = uptime({
  8: { downtime: 35, incidents: [{ title: "Zahlungen schlugen fehl", minutes: 35 }] },
  41: { degraded: 70 },
});

export default function UptimeLabels() {
  return (
    <Uptime
      data={days}
      label="Zahlungen"
      locale="de-DE"
      labels={{
        status: {
          up: "Betriebsbereit",
          degraded: "Beeinträchtigt",
          down: "Ausfall",
          none: "Keine Daten",
        },
        uptime: (percent) => `${percent} Verfügbarkeit`,
        daysAgo: (count) => (count === 1 ? "vor 1 Tag" : `vor ${count} Tagen`),
        today: "Heute",
      }}
      className="max-w-xl"
    >
      <UptimeLabel>Zahlungen</UptimeLabel>
      <UptimeStatus />
      <UptimeTrack />
      <UptimeFooter />
    </Uptime>
  );
}
```

## Props

| Part | Extends |
| --- | --- |
| `Uptime` | `<div>` |
| `UptimeLabel` | `<span>` |
| `UptimeStatus` | [`Status`](https://ui.jarv.is/components/status.md) |
| `UptimeTrack` | `<div>` |
| `UptimeFooter` | `<div>` |

The tables leave out `className`, `style` and `render`. Parts built on Base UI take them as its [styling](https://base-ui.com/react/handbook/styling) and [composition](https://base-ui.com/react/handbook/composition) guides describe.

### Uptime

| Prop           | Type                                                                                      | Description                                                                                                                                                                              |
| -------------- | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `data`         | `UptimeDay[]`                                                                             | One entry per day, oldest first, ending with today.                                                                                                                                      |
| `days?`        | `number`                                                                                  | How many days to show and to work out the uptime over, which defaults to the length of `data`. Days before the first in `data` show as no data, so strips of different services line up. |
| `minBarWidth?` | `number`                                                                                  | The narrowest a bar can be, in pixels. A strip too narrow for every day at this width shows as many of the latest as fit. Default: `3`                                                   |
| `current?`     | `"degraded" \| "down" \| "none" \| "up"`                                                  | The status shown now, which defaults to the last day's.                                                                                                                                  |
| `label?`       | `string`                                                                                  | Names the strip for screen readers. Default: `"Uptime"`                                                                                                                                  |
| `labels?`      | `(Partial<Omit<UptimeLabels, "status">> & { status?: Partial<UptimeLabels["status"]>; })` | The words the strip shows and reads out, in place of the English ones.                                                                                                                   |
| `locale?`      | `string`                                                                                  | The BCP 47 locale that formats dates and percentages. Default: `"en-US"`                                                                                                                 |

### UptimeStatus

| Prop      | Type                                     | Description                                            |
| --------- | ---------------------------------------- | ------------------------------------------------------ |
| `status?` | `"degraded" \| "down" \| "none" \| "up"` | The status to show, which defaults to the current one. |

Each day in `data` takes these fields.

| Field       | Type                                     | Description                                                                                      |
| ----------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `date`      | `string`                                 | The day, as an ISO date such as `2026-09-15`.                                                    |
| `status`    | `"up" \| "degraded" \| "down" \| "none"` | The day's status, in place of the one worked out from its minutes. `none` means there's no data. |
| `downtime`  | `number`                                 | Minutes of outage, which count against uptime.                                                   |
| `degraded`  | `number`                                 | Minutes of degraded service, which show but don't count against uptime.                          |
| `uptime`    | `number`                                 | The day's uptime as a percentage, in place of the one worked out from `downtime`.                |
| `incidents` | `{ title: string; minutes?: number }[]`  | What happened, listed in the day's tooltip.                                                      |
