# Circuit: Webhook Pipeline

> A payment webhook through a queue and a worker to three destinations, fired by a button through `apiRef.send()` with `autoPlay={false}`. Each destination's trace carries its own `color`, and the delivery log fills from `onArrive`.

A shadcn/ui block from the [@keyframeui](https://keyframeui.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/keyframeui/circuit-pipeline-demo).

- Page, with a live preview: https://shadcn.exchange/r/keyframeui/circuit-pipeline-demo
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/keyframeui/circuit-pipeline-demo-light-a9208025bd8d43a9.png
- Registry item: https://keyframeui.com/r/circuit-pipeline-demo.json
- Categories: diagram, svg, interaction, landing
- Listed: 2026-10-15, source last changed 2026-10-15

## Install

```bash
npx shadcn@latest add https://keyframeui.com/r/circuit-pipeline-demo.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `https://keyframeui.com/r/circuit.json`

## Notes from the registry

Live preview, examples and props: https://keyframeui.com/components/circuit#circuit-pipeline-demo

## Files

### `registry/demos/circuit-pipeline-demo.tsx`

```tsx
"use client";

import * as React from "react";

import { Circuit, type CircuitControls } from "@/components/ui/circuit";

const STEPS = [
  { id: "stripe", label: "POST /webhooks/stripe", meta: "invoice.paid" },
  { id: "queue", label: "Queue", meta: "events · fifo" },
  { id: "worker", label: "Worker", meta: "handlePaid()" },
];

const SINKS = [
  { id: "db", label: "Postgres", meta: "upsert", color: "#34d399" },
  { id: "email", label: "Email", meta: "receipt", color: "#a78bfa" },
  { id: "slack", label: "Slack", meta: "#revenue", color: "#fbbf24" },
];

const card =
  "rounded-lg border border-zinc-200 bg-white px-3 py-2 transition-[opacity,border-color,box-shadow] duration-300 dark:border-zinc-800 dark:bg-zinc-900 data-[circuit-state=dimmed]:opacity-45 data-circuit-pulse:border-zinc-400 data-circuit-pulse:shadow-[0_0_0_4px_rgb(113_113_122/0.14)] dark:data-circuit-pulse:border-zinc-500";

type Entry = { id: number; at: string; text: string };

export function CircuitPipelineDemo() {
  const api = React.useRef<CircuitControls>(null);
  const [log, setLog] = React.useState<Entry[]>([]);
  const next = React.useRef(0);

  return (
    <div className="@container w-full max-w-3xl">
      <div className="grid gap-8 @2xl:grid-cols-[minmax(0,1fr)_15rem]">
        <Circuit
          apiRef={api}
          autoPlay={false}
          speed={320}
          flow={[
            "stripe -> queue -> worker",
            ...SINKS.map((s) => ({ from: "worker", to: s.id, color: s.color })),
          ]}
          onArrive={(node, from) => {
            const time = new Date().toLocaleTimeString([], { hour12: false });
            next.current += 1;
            const id = next.current;
            setLog((l) => [{ id, at: time, text: `${from} → ${node}` }, ...l].slice(0, 7));
          }}
          packetClassName="text-zinc-900 dark:text-zinc-100"
          className="flex flex-col items-center gap-9"
        >
          {STEPS.map((s) => (
            <div key={s.id} data-circuit={s.id} className={`${card} w-56 text-center`}>
              <p className="font-mono text-xs font-medium text-zinc-900 dark:text-zinc-100">{s.label}</p>
              <p className="font-mono text-[11px] text-zinc-500">{s.meta}</p>
            </div>
          ))}
          <div className="grid w-full grid-cols-3 gap-3">
            {SINKS.map((s) => (
              <div key={s.id} data-circuit={s.id} className={`${card} text-center`}>
                <p className="flex items-center justify-center gap-1.5 text-xs font-medium text-zinc-900 dark:text-zinc-100">
                  <span className="size-1.5 rounded-full" style={{ background: s.color }} />
                  {s.label}
                </p>
                <p className="font-mono text-[11px] text-zinc-500">{s.meta}</p>
              </div>
            ))}
          </div>
        </Circuit>

        <div className="flex flex-col gap-3">
          <button
            type="button"
            onClick={() => api.current?.send("stripe")}
            className="h-9 rounded-lg bg-zinc-900 px-3 text-sm font-medium text-white transition-transform active:scale-[0.98] dark:bg-zinc-100 dark:text-zinc-900"
          >
            Send test event
          </button>
          <ol className="flex min-h-48 flex-col gap-1 rounded-lg border border-dashed border-zinc-200 p-3 font-mono text-[11px] dark:border-zinc-800">
            {log.length === 0 && <li className="text-zinc-400">No deliveries yet.</li>}
            {log.map((e) => (
              <li key={e.id} className="flex justify-between gap-3 text-zinc-600 dark:text-zinc-400">
                <span>{e.text}</span>
                <span className="text-zinc-400 dark:text-zinc-600">{e.at}</span>
              </li>
            ))}
          </ol>
        </div>
      </div>
    </div>
  );
}
```
