Widget reference
Buoy ships a set of React components plugin authors compose inside their render(ctx) callbacks. Every component is declared as a global by buoy-sdk.d.ts — no import needed.
For layout outside the widget catalog, plain DOM elements (<div>, <span>, <pre>) are also allowed.
KeyValue / Row
The most common widget. A titled section with label/value rows.
<KeyValue title="Quick facts">
<Row label="Name" value={obj.metadata.name} />
<Row label="Phase" value={obj.status?.phase} badge />
<Row label="Node" value={obj.spec.nodeName} default="(unscheduled)" />
<Row
label="Owner"
value={ref.name}
link={{ kind: "Deployment", name: ref.name, namespace: obj.metadata.namespace }}
/>
</KeyValue>
<Row> props:
| prop | type | notes |
|---|---|---|
label | string | The dt cell. |
value | unknown | Auto-formatted. Objects render as JSON. |
default | string | Rendered (muted) when value is null / undefined. |
mono | boolean | Monospace font. |
badge | boolean | Render as a colored StatusBadge. |
link | { kind, name, namespace?, resource? } | Cell becomes a click-through deep link. |
Pills / Pill
A horizontal row of colored chips. Use for headline status indicators.
<Pills title="State">
<Pill label={obj.status.phase} tone={obj.status.phase === "Ready" ? "ok" : "warn"} />
<Pill label={obj.spec.tier} tone="info" />
</Pills>
<Pill> tone: ok / error / warn / info / gray (synonyms accepted: good/green, bad/red, warning/yellow, blue).
Banner
One-line callout in a colored band. Conditionally render with regular JSX:
{obj.status.phase === "Failed" && (
<Banner tone="error" text={`Pod failed: ${obj.status.message}`} />
)}
Same tone palette as <Pill>.
Section
Wrap a group of widgets in a titled container, optionally collapsible.
<Section title="Advanced" collapsible defaultOpen={false}>
<KeyValue><Row label="UID" value={obj.metadata.uid} mono /></KeyValue>
</Section>
Conditions
Renders status.conditions[] as a tidy table. Hidden when the array is empty.
<Conditions from={obj.status?.conditions} />
The from prop accepts any array — usually status.conditions, but plenty of CRDs put conditions elsewhere.
Progress
Single horizontal progress bar.
<Progress
title="Replicas"
value={obj.status?.readyReplicas}
max={obj.spec.replicas}
/>
value and max are coerced numerically; non-numbers fall back to 0 / 1.
StackedBar / Segment
N-segment bar for “buckets of a whole” visualizations (canary/stable weights, blue/green, status histograms).
<StackedBar title="Weights">
<Segment value={obj.status.canary.weight} tone="info" label="canary" />
<Segment value={100 - obj.status.canary.weight} tone="ok" label="stable" />
</StackedBar>
total overrides the implicit denominator (sum of segment values).
Table
Generic data table. You supply rows (any array) and columns describing each cell.
<Table
title="Steps"
rows={obj.spec.strategy.canary.steps}
columns={[
{ header: "Step", cell: (_s, i) => `${i + 1}` },
{ header: "Weight", cell: (s) => s.setWeight ?? "—" },
{ header: "Pause", cell: (s) => s.pause?.duration ?? "" },
]}
empty="No steps configured"
/>
Each column’s cell(row, idx) returns a ReactNode — return JSX, strings, numbers, or <CellValue> for the same auto-formatting / link / badge handling as <Row>.
RelatedList
Live watch over related resources by selector. Without custom columns, reuses the native ResourceTable so the embedded list looks like every other Buoy table.
<RelatedList
title="Pods"
kind="pods"
labelSelector={obj.spec.selector?.matchLabels}
/>
With custom columns:
<RelatedList
title="Owned ConfigMaps"
kind="configmaps"
labelSelector={{ "app.kubernetes.io/instance": obj.metadata.name }}
columns={[
{ header: "NAME", cell: (row) => row.name },
{ header: "KEYS", cell: (row) => Object.keys(row.object.data ?? {}).length },
]}
/>
labelSelector accepts a kubectl-style string ("app=foo,tier=bar") or an object that’s joined the same way.
RelatedObject
Inline link card pointing to another resource. Children render alongside the link.
<RelatedObject
title="Pod"
kind="Pod"
resource="pods"
name={obj.status.podRef?.name}
namespace={obj.metadata.namespace}
/>
Yaml
Pretty-prints a value (the whole object or a sub-tree) in the same CodeMirror viewer used elsewhere in Buoy.
<Yaml title="Spec" value={obj.spec} />
Markdown
Plain-text body in a monospace block. No markdown parsing (avoids the dep + XSS surface) — if you need structured content, use plain JSX.
<Markdown title="Notes" body={obj.status.notes} />
CellValue
The same auto-formatter <Row> and <Table> use under the hood. Call it directly when you need that behavior inside a custom JSX expression:
<CellValue value={obj.spec.image} mono link={{ kind: "Image", name: obj.spec.image }} />
Hooks
Call inside render(ctx) like any React hook.
useObject({ resource, name, namespace?, context? })
Live single-object watch. namespace / context default to the detail view’s. Returns { data, loading, error }.
const owner = useObject({ resource: "deployments", name: ownerRefName });
if (owner.loading) return <Banner tone="info" text="Loading owner…" />;
useList(resource, opts?)
Live list watch. opts.labelSelector accepts a string or an object; opts.allNamespaces overrides the namespace default. Returns the standard watch state ({ rows, columns, ready, error }).
const peers = useList("pods", {
labelSelector: { app: obj.metadata.labels.app },
});
Hooks throw if called outside a plugin render(ctx) callback.