React & client

The dashboard is built on three pieces that ship on their own: React hooks, a typed browser client, and a headless kit for rendering spans. Anything the dashboard shows, you can build, against the same contract as bc.api, over HTTP.

React hooks

npm i @breadcrumb-sh/react

Inside <BreadcrumbDashboard> — including on your own custom pages — the hooks need no setup. To build a UI from scratch instead, wrap it in a BreadcrumbProvider inside a react-query QueryClientProvider:

import { QueryClientProvider } from "@tanstack/react-query";
import { BreadcrumbProvider, useSessions, useStats } from "@breadcrumb-sh/react";

function App() {
  return (
    <QueryClientProvider client={qc}>
      <BreadcrumbProvider basePath="/api/breadcrumb">
        <Traces />
      </BreadcrumbProvider>
    </QueryClientProvider>
  );
}

function Traces() {
  const { data: stats } = useStats({ since: Date.now() - 7 * 864e5 });
  const { data } = useSessions({ environment: "production" });
  // …render it however your product looks.
}

Each hook takes the same filters as bc.api plus an optional TanStack Query options object, and returns a UseQueryResult.

HookReturns
useSessions(filter?)A page of session summaries.
useTraces(filter?)A page of trace summaries.
useRuns(sessionKey)The runs within a session.
useTrace(traceId)Every span in a trace.
useSpan(id)A single span.
useCost({ days?, environment? })The Cost view’s data.
useStats(filter?)Runs, error rate, cost, tokens, latency.
useEnvironments()Known environment names.
useMcpKeys()MCP keys, plus the server name for connect snippets.
useCreateMcpKey() / useRevokeMcpKey()Mutations for the same.

useBreadcrumbClient() returns the underlying client for imperative calls.

Typed client

Framework-agnostic — use it anywhere in the browser or in another service.

import { createBreadcrumbClient } from "@breadcrumb-sh/core/client";

const client = createBreadcrumbClient({ basePath: "/api/breadcrumb" });
const { items } = await client.listSessions({ status: "error" });

Pass a configured client to <BreadcrumbDashboard client={…}> when you need custom fetch behaviour or auth headers on every request.

Headless kit

The view logic — nobody should rebuild span-tree assembly. Import it and render your own components.

import { traceModel, selfTime, hotspots, asMessages, fmtCost } from "@breadcrumb-sh/core/kit";

const model = traceModel(spans);      // rows, scales, hotspots, totals
model.rows;                           // denoised, depth-indexed, ready to map
const { errorId, slowestId } = hotspots(spans);
selfTime(span, children);             // extent minus what the children covered
const chat = asMessages(span.input);  // detect + parse chat-shaped payloads
fmtCost(0.0042);                      // "$0.0042"

mode picks the shape: "flow" (denoised, the default), "full" (every span), or "timeline" (flat, ordered by start time, so concurrent steps sit next to each other). Pass collapsed — a set of span ids — to fold subtrees away, and seed it from defaultCollapsed(spans, mode) for the folded-by-default reading the dashboard uses. Each row reports hasChildren, collapsed, and hiddenCount so you can draw the affordance.

import { defaultCollapsed, lastActivity, traceModel } from "@breadcrumb-sh/core/kit";

const [collapsed, setCollapsed] = useState(() => defaultCollapsed(spans));
const model = traceModel(spans, { mode: "timeline", collapsed });

// Spans are stored as they end, so a run still writing is worth polling for.
const running = Date.now() - lastActivity(spans) < 30_000;

traceModel is what the shipped waterfall renders from, so a UI you build from scratch reads exactly the same numbers rather than reimplementing them.

Three levels, one contract underneath: server SDK (bc.api) → typed client → React hooks, with the kit for rendering. Pick whichever fits your app.