UI kit

IngestStatus

IngestionReact · Next.js

Hits your /health endpoint and shows whether the service is online and whether the index has been built, with optional polling. A quick way to surface backend readiness in your app.

<IngestStatus />

Live preview

Checking…

Installation

No npm dependencies — just React + Tailwind CSS. There are two ways to use it:

  • React — copy the source below; it calls your pipeline directly from the browser.
  • Next.js — switch to the Next.js tab in the source below; it keeps your pipeline URL + key server-side behind a Route Handler (adds a couple of .env.local vars).

Usage

usage.tsx
import { IngestStatus } from "@/components/IngestStatus";

export default function Example() {
  return (
    <IngestStatus endpoint="https://api.example.com/health" pollMs={5000} />
  );
}

Props

PropTypeDefaultDescription
endpointrequiredstringHealth URL. GET → { status, pipeline_loaded }.
pollMsnumber0Poll interval in ms (0 = fetch once).
fetchertypeof fetchOverride the transport. Defaults to window.fetch.

Component source copy-paste ready

IngestStatus.tsx
"use client";

import { useEffect, useState } from "react";

interface HealthData {
  status?: string;
  pipeline_loaded?: boolean;
  [k: string]: unknown;
}

interface IngestStatusProps {
  /** Health endpoint. GET → { status, pipeline_loaded, … }. */
  endpoint: string;
  /** Poll interval in ms (0 = fetch once). Default: 0. */
  pollMs?: number;
  /** Override the transport (e.g. for testing). Defaults to window.fetch. */
  fetcher?: typeof fetch;
  className?: string;
}

/**
 * IngestStatus — a live status pill for your pipeline. Hits the /health
 * endpoint and shows whether the service is online and whether the index has
 * been built, with optional polling.
 */
export function IngestStatus({ endpoint, pollMs = 0, fetcher, className = "" }: IngestStatusProps) {
  const [state, setState] = useState<"loading" | "ok" | "down">("loading");
  const [data, setData] = useState<HealthData | null>(null);

  useEffect(() => {
    let alive = true;
    const f = fetcher ?? fetch;

    async function check() {
      try {
        const res = await f(endpoint);
        const json = (await res.json()) as HealthData;
        if (!alive) return;
        setData(json);
        setState(res.ok && json.status !== "error" ? "ok" : "down");
      } catch {
        if (alive) setState("down");
      }
    }

    check();
    const id = pollMs > 0 ? setInterval(check, pollMs) : undefined;
    return () => {
      alive = false;
      if (id) clearInterval(id);
    };
  }, [endpoint, pollMs, fetcher]);

  const dot = state === "ok" ? "#10b981" : state === "down" ? "#ef4444" : "#f59e0b";
  const label = state === "ok" ? "Pipeline online" : state === "down" ? "Pipeline unreachable" : "Checking…";

  return (
    <div className={`inline-flex items-center gap-2.5 rounded-lg border border-zinc-200 bg-white px-3 py-2 text-sm dark:border-zinc-800 dark:bg-zinc-900 ${className}`}>
      <span className="relative flex h-2.5 w-2.5">
        {state === "loading" && (
          <span className="absolute inline-flex h-full w-full animate-ping rounded-full opacity-60" style={{ background: dot }} />
        )}
        <span className="relative inline-flex h-2.5 w-2.5 rounded-full" style={{ background: dot }} />
      </span>
      <span className="font-medium text-zinc-700 dark:text-zinc-200">{label}</span>
      {state === "ok" && (
        <span className="text-xs text-zinc-400">{data?.pipeline_loaded ? "index ready" : "awaiting ingest"}</span>
      )}
    </div>
  );
}