KDS
Workflow

Node

Card visual de um nó de workflow — handles configuráveis, header, content, footer.

Overview

Node é o card visual de um nó dentro de Canvas. Renderiza handles (target / source) configuráveis e mantém composição similar a Card — Header com Title + Description + Action, Content, Footer.

Use como nodeTypes.<key> no Canvas. Cada Node aceita handles: { target, source } pra controlar conexões direcionais.

Preview
Tool node com handles target + source.
Buscar contexto
tool · search

Vector search em embeddings.

$0.0012 / call

Anatomy

<Node handles={{ target, source }}>
  ├─ <NodeHeader>
  │    ├─ <NodeTitle>...</NodeTitle>
  │    ├─ <NodeDescription>...</NodeDescription>
  │    └─ <NodeAction>...</NodeAction>
  ├─ <NodeContent>...</NodeContent>
  └─ <NodeFooter>...</NodeFooter>
</Node>

Usage

Defina como custom node type:

import { Node, NodeContent, NodeDescription, NodeHeader, NodeTitle } from "@kalvner/kds/ai/node";

function ToolNode({ data }) {
  return (
    <Node handles={{ target: true, source: true }}>
      <NodeHeader>
        <NodeTitle>{data.label}</NodeTitle>
        <NodeDescription>{data.toolType}</NodeDescription>
      </NodeHeader>
      <NodeContent>
        <p className="text-sm text-muted-foreground">{data.description}</p>
      </NodeContent>
    </Node>
  );
}

<Canvas nodes={nodes} edges={edges} nodeTypes={{ tool: ToolNode }} />

Props

Node (Root)

PropTipoDefaultDescrição
handles{ target: boolean; source: boolean }Controla quais handles aparecem. target na esquerda (input), source na direita (output).
classNamestringOverride (default tem gap-0 rounded-md p-0).

Herda de Card.

NodeHeader

Aceita ComponentProps<typeof CardHeader>. Default tem bg-secondary + border-bottom.

NodeTitle / NodeDescription

Aceitam mesmos props de CardTitle / CardDescription.

NodeAction

Aceita ComponentProps<typeof CardAction>. Encaixa botão no canto direito do header.

NodeContent

Aceita ComponentProps<typeof CardContent>. Default tem p-3.

NodeFooter

Aceita ComponentProps<typeof CardFooter>. Default tem border-t + p-3.

Subcomponents

  • Node — root. handles define direção das conexões. Sem ambos target e source true, o node fica sem handles (visual decorativo).
  • NodeHeader — slot superior com cor secondary.
  • NodeTitle + NodeDescription — texto do header.
  • NodeAction — slot pra botão (toolbar trigger, dropdown).
  • NodeContent — corpo do node.
  • NodeFooter — meta footer (custo, latência, status).

States

handles define o comportamento de conexão:

  • { target: true, source: false } — node terminal (output, sem saída). Padrão de "answer".
  • { target: false, source: true } — node de entrada (input, sem entrada). Padrão de "user prompt".
  • { target: true, source: true } — node intermediário.
  • { target: false, source: false } — decorativo (sem conexões).

Composition

Tool node com toolbar contextual

function ToolNode({ data, selected }) {
  return (
    <Node handles={{ target: true, source: true }} selected={selected}>
      <Toolbar isVisible={selected} nodeId={data.id}>
        <Button variant="ghost" size="icon"><Copy /></Button>
        <Button variant="ghost" size="icon"><Trash /></Button>
      </Toolbar>
      <NodeHeader>
        <NodeTitle>{data.label}</NodeTitle>
        <NodeDescription>tool · {data.toolName}</NodeDescription>
      </NodeHeader>
      <NodeContent>{data.summary}</NodeContent>
      <NodeFooter>
        <span className="text-xs text-muted-foreground">${data.cost}</span>
      </NodeFooter>
    </Node>
  );
}

When to use

  • Custom node types em workflows visuais.
  • Cards com handles direcionais (input/output).

When not to use

  • Card sem conexões — use Card direto.
  • Lista linear — use Plan ou Task.

Best practices

  • handles direcional. Decida claramente se o node é input, output ou intermediate antes de configurar.
  • NodeFooter pra meta. Custo, latência, status — tudo que ajuda observabilidade.
  • Selected state visual — combine com Toolbar que aparece só quando selected.

Accessibility

ConcernComportamento
RolesCard é <div> neutro; embrulhe em <article> se hierarquia importa.
HandlesRenderizados via Radix internals do ReactFlow.
KeyboardReactFlow lida com nav (Tab + Arrow).

On this page