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.
| Hook | Returns |
|---|---|
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.