Skip to content

Health Chart

A health or uptime chart that maps datapoints onto a fixed time range.

<script lang="ts">
  import {
    HealthChart,
    type DataPoint
  } from "$lib/components/ui/health-chart/index.js";
  import { DateTime } from "luxon";
 
  const day = DateTime.now().startOf("day");
 
  const data: DataPoint[] = [
    {
      status: "orange",
      description: "Image processing latency increased",
      timestamp: day.minus({ days: 52 }).plus({ hours: 8 }).toJSDate()
    },
    {
      status: "red",
      description: "Checkout writes were unavailable",
      timestamp: day.minus({ days: 41 }).plus({ hours: 3 }).toJSDate()
    },
    {
      status: "orange",
      description: "Checkout writes recovered with degraded queue depth",
      timestamp: day.minus({ days: 41 }).plus({ hours: 16 }).toJSDate()
    },
    {
      status: "orange",
      description: "Search indexing lagged behind ingestion",
      timestamp: day.minus({ days: 24 }).plus({ hours: 11 }).toJSDate()
    },
    {
      status: "red",
      description: "Public API returned elevated 5xx responses",
      timestamp: day.minus({ days: 9 }).plus({ hours: 5 }).toJSDate()
    },
    {
      status: "green",
      description: "Synthetic checks passed after remediation",
      timestamp: day.minus({ days: 9 }).plus({ hours: 21 }).toJSDate()
    }
  ];
</script>
 
<HealthChart
  {data}
  range="60d"
  emptyStatus="green"
  emptyText="No incident recorded"
  class="max-w-2xl"
/>

Each bar covers a fixed time slot within the selected range. A datapoint's timestamp decides which bar it lands in; when several land in the same slot, the latest by timestamp is shown. Empty slots take the emptyStatus color.

Installation

pnpm dlx jsrepo@latest add health-chart

Usage

<script lang="ts">
  import { HealthChart, type DataPoint } from '$lib/components/ui/health-chart/index.js';
  import { DateTime } from 'luxon';
 
  const day = DateTime.now().startOf('day');
 
  const data: DataPoint[] = [
    {
      status: 'red',
      description: 'Public API returned elevated 5xx responses',
      timestamp: day.minus({ days: 9 }).plus({ hours: 5 }).toJSDate()
    },
    {
      status: 'green',
      description: 'Synthetic checks passed after remediation',
      timestamp: day.minus({ days: 9 }).plus({ hours: 21 }).toJSDate()
    }
  ];
</script>
 
<HealthChart {data} range="60d" emptyStatus="green" emptyText="No incident recorded" />

Data Model

type DataPoint = {
  status: 'red' | 'orange' | 'green' | 'gray';
  description: string;
  timestamp: Date;
};

Data may be unsorted.

Examples

Ranges

60m
24h
30d
60d
<script lang="ts">
  import {
    HealthChart,
    type DataPoint,
    type HealthChartRange
  } from "$lib/components/ui/health-chart/index.js";
  import { DateTime } from "luxon";
 
  const now = DateTime.now();
  const minute = now.startOf("minute");
  const hour = now.startOf("hour");
  const day = now.startOf("day");
 
  function point(
    status: DataPoint["status"],
    description: string,
    timestamp: DateTime
  ): DataPoint {
    return { status, description, timestamp: timestamp.toJSDate() };
  }
 
  const rows: { range: HealthChartRange; data: DataPoint[] }[] = [
    {
      range: "60m",
      data: [
        point(
          "orange",
          "Queue depth rose above threshold",
          minute.minus({ minutes: 47 }).plus({ seconds: 18 })
        ),
        point(
          "red",
          "Worker pool stopped accepting jobs",
          minute.minus({ minutes: 21 }).plus({ seconds: 33 })
        ),
        point(
          "green",
          "Workers recovered",
          minute.minus({ minutes: 4 }).plus({ seconds: 12 })
        )
      ]
    },
    {
      range: "24h",
      data: [
        point(
          "orange",
          "Deploy verification slowed down",
          hour.minus({ hours: 22 }).plus({ minutes: 9 })
        ),
        point(
          "red",
          "Webhook delivery paused",
          hour.minus({ hours: 8 }).plus({ minutes: 24 })
        ),
        point(
          "green",
          "Webhook delivery resumed",
          hour.minus({ hours: 2 }).plus({ minutes: 41 })
        )
      ]
    },
    {
      range: "30d",
      data: [
        point(
          "orange",
          "Background jobs delayed",
          day.minus({ days: 25 }).plus({ hours: 10 })
        ),
        point(
          "red",
          "Exports unavailable",
          day.minus({ days: 17 }).plus({ hours: 6 })
        ),
        point(
          "orange",
          "Search freshness delayed",
          day.minus({ days: 6 }).plus({ hours: 15 })
        )
      ]
    },
    {
      range: "60d",
      data: [
        point(
          "orange",
          "Scheduled maintenance exceeded window",
          day.minus({ days: 54 }).plus({ hours: 9 })
        ),
        point(
          "red",
          "Upload API unavailable",
          day.minus({ days: 38 }).plus({ hours: 13 })
        ),
        point(
          "orange",
          "Notifications degraded",
          day.minus({ days: 18 }).plus({ hours: 4 })
        )
      ]
    }
  ];
</script>
 
<div class="flex w-full max-w-2xl flex-col gap-3">
  {#each rows as row (row.range)}
    <div class="grid grid-cols-[3rem_1fr] items-center gap-3">
      <span class="font-mono text-xs text-muted-foreground">{row.range}</span>
      <HealthChart
        data={row.data}
        range={row.range}
        emptyStatus="gray"
        emptyText="No sample"
        class="h-7"
      />
    </div>
  {/each}
</div>
Range Bars Slot size
60m 60 1 minute
24h 24 1 hour
30d 30 1 day
60d 60 1 day

Minute ranges anchor to the current minute, hour ranges to the current hour, and day ranges to the start of today.

Empty state

emptyStatus="green"
emptyStatus="gray"
<script lang="ts">
  import {
    HealthChart,
    type DataPoint
  } from "$lib/components/ui/health-chart/index.js";
  import { DateTime } from "luxon";
 
  const day = DateTime.now().startOf("day");
 
  function point(
    status: DataPoint["status"],
    description: string,
    timestamp: DateTime
  ): DataPoint {
    return { status, description, timestamp: timestamp.toJSDate() };
  }
 
  const data: DataPoint[] = [
    point(
      "orange",
      "Read replicas lagged behind primary",
      day.minus({ days: 24 }).plus({ hours: 7 })
    ),
    point(
      "red",
      "Billing API requests timed out",
      day.minus({ days: 13 }).plus({ hours: 11 })
    ),
    point(
      "orange",
      "Billing API recovered with elevated latency",
      day.minus({ days: 13 }).plus({ hours: 20 })
    ),
    point(
      "red",
      "Ingestion endpoint rejected events",
      day.minus({ days: 4 }).plus({ hours: 3 })
    )
  ];
</script>
 
<div class="grid w-full max-w-2xl gap-4 sm:grid-cols-2">
  <div class="flex flex-col gap-2">
    <span class="font-mono text-xs text-muted-foreground"
      >emptyStatus="green"</span
    >
    <HealthChart
      {data}
      range="30d"
      emptyStatus="green"
      emptyText="No incident recorded"
    />
  </div>
  <div class="flex flex-col gap-2">
    <span class="font-mono text-xs text-muted-foreground"
      >emptyStatus="gray"</span
    >
    <HealthChart
      {data}
      range="30d"
      emptyStatus="gray"
      emptyText="No sample recorded"
    />
  </div>
</div>

Use emptyStatus="green" when your data records incidents only, an empty slot means nothing went wrong. Use emptyStatus="gray" when your data is sampled, an empty slot means no sample exists.

Props

Prop Type Default Description
data DataPoint[] [] Datapoints plotted within the visible range.
range '60m' | '24h' | '30d' | '60d' '60d' The visible time range.
emptyStatus 'green' | 'orange' | 'red' | 'gray' 'gray' Status color for slots without a datapoint.
emptyText string 'No data' Tooltip text for slots without a datapoint.
class ClassValue - Classes merged onto the chart root.

The component also accepts native div attributes.