Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelogContact
Components
Buttons & Actions
ButtonButton GroupCopy ButtonDelete ButtonTheme ToggleToggleToggle Group
Inputs
Avatar UploadCalendarCheckboxColor PickerComboboxCountry SelectCron PickerCurrency InputDate PickerDate Range PickerDate Time PickerDuration PickerEmail InputEmoji PickerFile UploadFilter BuilderInputInput GroupListboxMasked InputMention InputMonth PickerMulti SelectNumber InputOTP InputPassword InputPhone InputRadio GroupRatingSearch InputSegmented ControlSelectSignature PadSliderSwitchTag InputTextareaTime PickerTimezone SelectTree Select
Forms
Address InputBooking WidgetCSV ImportFieldForm WizardInline EditInvoice line itemsRich Text EditorSchema Builder
Navigation
Back To TopBounce SidebarBreadcrumbCommand MenuGlobal SearchGooey NavHook SidebarNav MenuNav TabsPaginationProximity SidebarScroll ProgressSidebarStepsTable Of ContentsTabsTopbarUser MenuWorkspace Switcher
Overlays
Alert DialogContext MenuDialogDrawerDropdown MenuFamily DrawerHover CardPopoverShare DialogSheetShortcuts DialogTooltipTour
Feedback
AlertBannerCookie ConsentEmoji ReactionEmpty StateNotification BellNotification CenterProgressRoute ProgressSetup ChecklistSkeletonSpinnerToast
Data Display
Approval FlowAvatarBadgeCalendar ViewCode BlockComment ThreadContact cardCountdownDescription ListDiff ViewerFile ListFile managerFolderGanttJSON ViewerKbdLog ViewerMarkdownPresenceSchedulerTagTimelineTree View
Tables & Lists
Audit LogData TableDeal PipelineEditable GridKanbanListMembers tablePermissions MatrixTableTask ListVirtual List
Charts & Stats
Animated CounterArea chartBar chartDonut chartFunnel chartGaugeGitHub ActivityHeatmap calendarLeaderboardLine chartRadar chartRadial barSparklineStat CardTrackerTrend badge
Layout
AccordionApp ShellCardCollapsibleDetail LayoutEntity HeaderPage HeaderScroll AreaSeparatorSettings LayoutSplit ViewToolbar
Media
Audio PlayerCarouselCSV ViewerDOCX EditorDOCX ViewerExcel ViewerFile SystemImage compareImage CropperImage GalleryImage LightboxLocation MapPDF ViewerPowerPoint ViewerQR CodeStep PlayerVideo Player
AI Kit
Agent Status CardChat MessageChat ThreadChat WidgetConversation ListFluid OrbGrid RevealMatrix OrbModel PickerPrompt InputReasoning IndicatorRule BuilderSuggestion ChipsTool Call TimelineUsage MeterVoice InputWorkflow Canvas
Text & Effects
Background PatternGravity LettersMarqueeTerminalText AnimateText Rotate
Mobile
Action SheetBottom Tab BarFABHaptic ButtonHero HeaderInstall PromptLarge Title HeaderNotification BannerOnboarding CarouselPicker WheelPull To RefreshSlide To ConfirmSticky CTA BarSwipe DeckSwipeable Tabs
Commerce
Cart drawerCategory pickerCheckout stepsCoupon inputFavorite buttonFilter panelListing cardListing gridOrder trackerPricePrice rangePricing tableProduct galleryQuantity stepperReviewsSeller card
Marketing Sections
App Download SectionBlog Cards SectionCTA SectionFAQ SectionFeature GridFeature ShowcaseHero SectionIntegrations SectionLogo CloudMarketing FooterMarketing HeaderMarketing SectionNewsletter SectionPricing SectionStats SectionSteps SectionTeam SectionTestimonials SectionTimeline SectionVideo Section
Blocks
Admin Dashboard PageAdmin ShellAI Chat PageAPI Keys PageAutomation Builder PageCheckout PageCRM Contact PageCRM Pipeline PageError PagesInbox PageInvoice PageKPI DashboardListing Detail PageLogin PageMarketplace Home PageMarketplace Search PageMarketplace ShellMobile App HomeOnboarding FlowOrder History PagePost Listing WizardPricing PageProfile PageRegister PageSettings PageUsers Admin Page
Templates
About PageAcademy PageAccessibility PageAffiliate PageAgency PageAI Product PageAppointment Booking PageBlog PageBlog Post PageBug Bounty PageCareers PageChangelog PageCommunity PageCompare PageContact PageCustomer Story PageCustomers PageDemo Booking PageDevelopers PageDocs Home PageDownload PageEnterprise PageEvents PageExperts PageFAQ PageFeature PageGlossary PageHelp Article PageHelp Center PageHome Services PageImpact PageIntegration Detail PageIntegrations PageJob Detail PageLanding PageLaunch PageLegal PageManifesto PagePortfolio PagePress PageProduct Tour PagePromo PageReal Estate PageReferral PageResearch Report PageRestaurant PageRoadmap PageROI Calculator PageSecurity PageSolutions PageStartups PageStatus PageStorefront PageSwitch PageTemplate Gallery PageTravel PageWaitlist PageYear in Review Page
Hooks & Utilities
AI ChatChart kitCommerceDate utilsFile exportFile typesFiltersForm validationHapticsInvoicingMotion presetsNavigationNumber formatRelative timeRomanian locationsSpreadsheetText matchTonesUIProvideruseClipboarduseConsentuseControllableStateuseCooldownuseDebounceuseKeyboardInsetuseLocaleuseLongPressuseMediaControlsuseMediaQueryuseOverflowItemsusePersistedStateuseScrollEdgesuseScrollStateuseSelectionuseShortcutuseStickToBottom
GalleryTheme

AI Kit

Workflow Canvas

AI Kit

Workflow Canvas React component for Tailwind CSS v4

Voice InputBackground Pattern
Live props
State
defaultSelectedId
readOnly
running
Layout
direction
Appearance
showMinimap
showControls
background
tone
color
Behavior
snapToGrid
fitOnMount
inspector
confirmDelete
Code
import { WorkflowCanvas } from "@/components/ui/workflow-canvas"

<WorkflowCanvas />
  1. Home
  2. Components
  3. AI Kit
  4. Workflow Canvas
Pro

Workflow Canvas React component for Tailwind CSS v4

v1.0.0·Added in 1.0.0

A React workflow builder: a node editor for automations with a trigger, actions, AI steps and yes / no branches you connect and watch run.

Steps are cards on a dotted canvas, joined by rounded step lines; a condition splits into Yes and No branches with a label on each line. Drag the canvas to pan, scroll or swipe with two fingers to pan, pinch (or Ctrl and the wheel) to zoom. Click a step to select it and open its settings in a panel on the right; drag it to move it (it snaps to a 16px grid and the move is one undo step). Drag from the dot under a step to another step to connect them: a connection into a trigger, one that would make a loop or a second line from the same branch is refused and the line turns red. Drop the line on empty space and the add menu opens there. Hover a line (or select it) for its + button, which inserts a step in between; a step with nothing after it shows a dashed + stub, and a branch that goes nowhere names itself next to it. The add menu searches the step catalog by name, kind or group. Right-click a step for Duplicate, Skip and Delete, or the empty canvas to add a step where you clicked; deleting a connected step asks first and a step in the middle hands its parent to its child. The toolbar adds (after the selected step, on the selected line, or in a free spot), undoes and redoes, tidies the layout (steps glide to their new places), zooms, fits and locks, and shows Run when onRun is set. Shift and click, or Shift and a drag on the canvas, selects several steps; Delete then asks once for all of them. In run mode editing locks, the line into the running step flows, markers turn from queued to running to done or failed, the view follows the running step unless you just panned, and a run that ends with every step done pops its markers once. On a phone one finger pans and two pinch; tap a step to select it, then drag it (an unselected step never moves by accident); long-press a step for its menu or the empty canvas to add a step; handles take 44px of finger reaching away from the card; the toolbar centers above the home indicator with 44px buttons, folding tidy, zoom and lock into a menu; settings open in a sheet that stops at half height, the selected step scrolls above it, and the canvas stays usable. With a keyboard: Tab reaches the canvas, the toolbar (arrow keys move inside it) and every step and connection; Enter or Space selects, Enter again moves into the settings and Escape goes back; arrows move the selected step (Shift for 4 times as far); Delete or Backspace removes the selection; A adds a step after the selection or on the selected line; Ctrl or Cmd with Z undoes, with Shift and Z (or Y) redoes, with D duplicates and with A selects everything; + and - zoom, Shift and 1 fits; the menu key or Shift and F10 opens a step's menu. Validation problems ring the step in red and are listed in its tooltip and settings; changes are announced to screen readers.

Workflow Canvas is a Pro React component in the React AI components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add workflow-canvas, together with Alert Dialog, Button, Drawer, Dropdown Menu, Input and 14 more and the npm packages @xyflow/react, motion and lucide-react.

Add to a project

Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup

npx wingo-ui@latest add workflow-canvas

Workflow Canvas is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans

Usage

Import WorkflowCanvas from @/components/ui/workflow-canvas.

import { useState } from "react"
import { CalendarClock, Flag, Mail, Search, Split } from "lucide-react"
import {
  WorkflowCanvas,
  layoutWorkflow,
  type WorkflowEdge,
  type WorkflowNodeDef,
  type WorkflowValue,
} from "@/components/ui/workflow-canvas"
// the component imports @xyflow/react/dist/base.css itself; Next and Vite accept a package css import in a client component
import { AlertDialogProvider } from "@/components/ui/alert-dialog"

// the step catalog, at module scope so it keeps its identity
const nodeTypes: WorkflowNodeDef[] = [
  { type: "schedule", kind: "trigger", label: "Daily at a set time", icon: <CalendarClock />, group: "Triggers" },
  { type: "find-invoices", kind: "action", label: "Find invoices", icon: <Search />, group: "CRM actions" },
  { type: "condition", kind: "condition", label: "Condition", icon: <Split />, group: "Logic" },
  { type: "send-email", kind: "action", label: "Send email", icon: <Mail />, group: "Communication" },
  { type: "end", kind: "end", label: "End", icon: <Flag />, group: "Logic" },
]

const edges: WorkflowEdge[] = [
  { id: "e1", source: "start", target: "find" },
  { id: "e2", source: "find", target: "check" },
  { id: "e3", source: "check", target: "send", sourceHandle: "yes" },
  { id: "e4", source: "check", target: "stop", sourceHandle: "no" },
]

const initial: WorkflowValue = {
  nodes: layoutWorkflow(
    [
      { id: "start", type: "schedule", position: { x: 0, y: 0 }, data: { title: "Daily at 9:00 AM" } },
      { id: "find", type: "find-invoices", position: { x: 0, y: 0 }, data: { title: "Find overdue invoices" } },
      { id: "check", type: "condition", position: { x: 0, y: 0 }, data: { title: "Balance over $1,000?" } },
      { id: "send", type: "send-email", position: { x: 0, y: 0 }, data: { title: "Send reminder", subtitle: "Template: Payment reminder" } },
      { id: "stop", type: "end", position: { x: 0, y: 0 }, data: { title: "Nothing to send" } },
    ],
    edges,
    { nodeTypes },
  ),
  edges,
}

export function AutomationEditor() {
  const [workflow, setWorkflow] = useState(initial)
  return (
    <div className="h-[560px]">
      <WorkflowCanvas
        nodeTypes={nodeTypes}
        value={workflow}
        onValueChange={setWorkflow}
        onRun={() => console.log("Run", workflow)}
        />
      {/* once near the root: confirms deleting a connected step */}
      <AlertDialogProvider />
    </div>
  )
}

Props

  • nodeTypes*
    WorkflowNodeDef[]

    The step catalog: { type, kind (trigger | action | condition | ai | delay | end), label, description, icon, group, tone, outputs, defaults, keywords }. It fills the add menu (grouped by group), gives each card its icon and kind, and a condition's outputs (Yes / No unless you name your own). Keep it at module scope.

  • value
    WorkflowValue

    The graph: { nodes: { id, type, position, data: { title, subtitle, status, error, config, disabled } }[], edges: { id, source, target, sourceHandle, label }[] }. A drag commits once, on drop; a value that arrives mid-drag waits until the drop. Steps that all sit at 0, 0 are laid out once.

  • defaultValue
    WorkflowValue

    The starting graph while uncontrolled; an empty canvas (the trigger picker) without it.

  • onValueChange
    (value: WorkflowValue) => void

    Every committed change: an add, a delete, a connection, a drop, a tidy, an inspector edit, an undo.

  • selectedId
    string | null

    The selected step, which opens the inspector. Several selected steps (Shift and click, a box) report null.

  • defaultSelectedIddefault ""
    string | null

    The step selected at first while uncontrolled, so its settings open with the canvas. The playground offers the steps of the demo workflow; "" selects nothing.

  • onSelectedIdChange
    (id: string | null) => void

    The selection changed. With inspector="none" this is where the app opens its own panel.

  • directiondefault "vertical"
    "vertical" | "horizontal"

    The flow: top to bottom (reads best on phones) or left to right. Handles, the dashed + stubs, the label of a branch and the tidy layout follow it.

  • readOnlydefault false
    boolean

    View only: pan, zoom, select and inspect. The editing tools, the + buttons and the step menu go away and the inspector shows its fields read only.

  • showMinimapdefault "auto"
    "auto" | "always" | "never"

    auto: from 768px, while part of the graph is outside the view (after zooming in or panning). always also shows it on phones, in the top corner. The minimap pans and zooms the canvas.

  • showControlsdefault true
    boolean

    The floating toolbar: add, undo, redo, tidy up, zoom, fit, lock and Run.

  • backgrounddefault "dots"
    "dots" | "lines" | "none"

    The pattern under the steps, drawn in a faint foreground tint so it works in both themes.

  • snapToGriddefault true
    boolean

    Dragged steps land on a 16px grid, and the arrow keys move a step by it (5px without the grid).

  • fitOnMountdefault true
    boolean

    Fits the whole graph into view on mount, clear of the toolbar and never above 100%, and again when a canvas that mounted hidden (a tab, a sheet) becomes visible. A flow too wide to read at that size (a long horizontal flow in a narrow column) opens at 50% from its trigger instead.

  • inspectordefault "auto"
    "auto" | "none"

    auto: the selected step's settings open in a panel on the right (at the bottom of a narrow canvas) and in a half-height sheet on phones. none: no built-in panel; show your own from onSelectedIdChange.

  • renderInspector
    (node: WorkflowNode, api: { update: (data: Partial<WorkflowNode["data"]>) => void; def: WorkflowNodeDef; fields: ReactNode; readOnly: boolean }) => ReactNode

    The inspector body for a step: its settings form. update merges into the step's data (one undo step per 500ms burst); fields is the default name, description and skip fields; readOnly is true while read only, locked or running.

  • onRun
    () => void

    Shows a Run button in the toolbar. Start the run, then drive running and each step's data.status.

  • runningdefault false
    boolean

    Run mode: editing locks, steps show node.data.status as a marker, the line into a running step flows, finished lines turn green, and the view follows the running step unless the person panned in the last 3 s.

  • confirmDeletedefault true
    boolean

    Asks before deleting a step that has connections (through useConfirm, so mount AlertDialogProvider once).

  • tonedefault "neutral"
    "neutral" | "brand" | "success" | "warning" | "danger" | "info"

    The selection ring, the dragged connection, a flowing line, the add button and the + buttons.

  • colordefault ""
    string

    Any CSS color; replaces the tone. Leave empty to use the tone.

  • labels
    Partial<WorkflowCanvasLabels>

    Every string, English by default (DEFAULT_WORKFLOW_CANVAS_LABELS): canvas, roleDescription, step, instructions, the toolbar's names (addStep, undo, redo, tidy, zoomIn, zoomOut, fit, lock, unlock, more, run, running), searchSteps, noSteps, emptyTitle, emptyDescription, insert, yes, no, the inspector's (inspector, title, subtitle, disabled, duplicate, delete, issuesTitle), deleteTitle(title), deleteManyTitle(count), deleteDescription, kinds, status, issues (the validation messages), announce, nodeLabel(step) and edgeLabel(from, to, label).

  • classNames
    Partial<Record<"flow" | "toolbar" | "minimap" | "menu" | "inspector" | "header" | "body" | "footer" | "empty" | "node" | "edgeLabel" | "insert", string>>

    Extra classes for the parts. The root takes className; drop its frame with className="rounded-none bg-transparent".

  • flowProps
    Partial<ReactFlowProps<WorkflowFlowNode, WorkflowFlowEdge>>

    Extra props for the underlying ReactFlow element (minZoom, maxZoom, zoomOnScroll, proOptions...). The xyflow attribution is hidden by default, which its MIT license allows; pass proOptions to show it.

  • className
    string

    Classes for the root, merged last. It fills its parent (at least 420px tall) in a rounded-3xl bg-card frame; give the parent a height.

Parts

  • WorkflowNodeCard

    component

    The look of one step on its own: the kind's icon tile, the kind, the title, the subtitle and the run marker, with the selected, invalid and skipped looks. For previews, template galleries and lists. Props: { node?: WorkflowNode; def?: WorkflowNodeDef; title?; subtitle?; kind?; icon?; status?; selected?; disabled?; invalid?; tone?; color?; labels?; classNames? }.

  • layoutWorkflow

    function

    A layered layout: ranks by the longest path from the trigger, a condition's branches side by side in output order, each row spread evenly around its parents. Returns the steps with new positions (top-left at 0, 0). Signature: (nodes: WorkflowNode[], edges: WorkflowEdge[], options?: { direction?; gapX? = 64; gapY? = 56; nodeWidth? = 260; nodeHeight? = 64; sizes?; nodeTypes? }) => WorkflowNode[].

  • validateWorkflow

    function

    The problems that stop an automation from running: no trigger or more than one, a connection into a trigger, loops, a condition branch that goes nowhere, unknown step types and connections to missing steps. Signature: (value: WorkflowValue, nodeTypes: WorkflowNodeDef[], messages?: Partial<WorkflowIssueMessages>) => { nodeId?: string; edgeId?: string; message: string }[].

  • createWorkflowNode

    function

    A new step from its definition, with a fresh id and the def's defaults (call it in handlers, not while rendering). Signature: (def: WorkflowNodeDef, position?: { x: number; y: number }) => WorkflowNode.

  • DEFAULT_WORKFLOW_CANVAS_LABELS

    constant

    The English strings, to spread under your translations. Type: WorkflowCanvasLabels.

  • DEFAULT_WORKFLOW_ISSUE_MESSAGES

    constant

    The English validation messages that validateWorkflow and the canvas use: noTrigger, multipleTriggers, triggerInput, cycle, missingNode, unknownType(type), unconnectedOutput(output), plus the "Yes" / "No" branch names. Pass a partial copy to validateWorkflow or as labels.issues to translate them. Type: WorkflowIssueMessages.

  • WorkflowValue

    type

    The automation: { nodes, edges }. A node is { id, type, position, data: { title, subtitle?, status?, error?, config?, disabled? } }; an edge is { id, source, target, sourceHandle?, label? }, where sourceHandle names a condition's output.

  • WorkflowNodeDef

    type

    One step type for the add menu: { type, kind, label, description?, icon, group?, tone?, outputs?, defaults?, keywords? }. kind is trigger, action, condition, ai, delay or end.

Examples

Horizontal, brand tone

direction="horizontal" for a left to right flow; tone="brand" colors the selection, the add button and the + buttons.

Empty

No steps yet: the canvas offers the triggers from nodeTypes, and only while there is none.

Read-only run history

readOnly with node.data.status from a finished run; pan, zoom and inspect, nothing moves.

Your own inspector

inspector="none" with selectedId and onSelectedIdChange: the app shows the step in its own side panel.

On a phone

At 390px the toolbar centers with 44px buttons and the settings open in a sheet that stops at half height.

Step cards on their own

WorkflowNodeCard for previews and template lists: every kind, running, failed, skipped and selected.

Dependencies

@xyflow/reactmotionlucide-reactalert-dialogbuttondrawerdropdown-menuinputlistboxpopoverspinnerswitchtextareatooltipuse-controllable-stateuse-long-pressuse-media-queryuse-shortcutai-chatmotiontonesui-config

Files

  • components/ui/workflow-canvas.tsx

FAQ

Is the React Workflow Canvas component free?

The preview, props and docs are free; copying the source needs a Pro plan. See plans.

How do I install Workflow Canvas?

Run npx wingo-ui@latest add workflow-canvas. It also adds the Wingo UI items it builds on (Alert Dialog, Button, Drawer, Dropdown Menu, Input and 14 more) and the npm packages @xyflow/react, motion and lucide-react. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/workflow-canvas.

How does Workflow Canvas behave on phones?

On a phone one finger pans and two pinch; tap a step to select it, then drag it (an unselected step never moves by accident); long-press a step for its menu or the empty canvas to add a step; handles take 44px of finger reaching away from the card; the toolbar centers above the home indicator with 44px buttons, folding tidy, zoom and lock into a menu.

See also

Rule BuilderTool Call TimelineKanbanDrawerPopoverAI Chatlib

Used in

Automation Builder Page

More React AI components

Voice InputModel PickerAgent Status CardChat MessageChat ThreadChat Widget
All 17 React AI components

Articles using this component

  • React Workflow Builder: Triggers, Branches and AI Steps

    Build it: screens and apps · Oct 16, 2026 · 13 min read

PreviousVoice InputNext Background Pattern

Tab moves between steps. Arrow keys move the selected step. Enter opens its settings. A adds a step. Delete removes the selection.

Mini map
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.
8 steps · 7 connections · no issues ·snap 16px · fit on mount · confirm delete on · inspector auto