Workflow
An example of how to use the AI Elements to build a workflow visualization.
An example of how to use the AI Elements to build a workflow visualization with interactive nodes and animated connections, built with React Flow.
"use client";import { Canvas } from "@/components/ai-elements/elements/canvas";import { Edge } from "@/components/ai-elements/elements/edge";import { Node, NodeContent, NodeDescription, NodeFooter, NodeHeader, NodeTitle,} from "@/components/ai-elements/elements/node";import { nanoid } from "nanoid";const nodeIds = { start: nanoid(), process1: nanoid(), process2: nanoid(), decision: nanoid(), output1: nanoid(), output2: nanoid(),};const nodes = [ { id: nodeIds.start, type: "workflow", position: { x: 0, y: 0 }, data: { label: "Start", description: "Initialize workflow", handles: { target: false, source: true }, }, }, { id: nodeIds.process1, type: "workflow", position: { x: 250, y: 0 }, data: { label: "Process Data", description: "Transform input", handles: { target: true, source: true }, }, }, { id: nodeIds.decision, type: "workflow", position: { x: 500, y: 0 }, data: { label: "Decision Point", description: "Route based on conditions", handles: { target: true, source: true }, }, }, { id: nodeIds.output1, type: "workflow", position: { x: 750, y: -100 }, data: { label: "Success Path", description: "Handle success case", handles: { target: true, source: true }, }, }, { id: nodeIds.output2, type: "workflow", position: { x: 750, y: 100 }, data: { label: "Error Path", description: "Handle error case", handles: { target: true, source: true }, }, }, { id: nodeIds.process2, type: "workflow", position: { x: 1000, y: 0 }, data: { label: "Complete", description: "Finalize workflow", handles: { target: true, source: false }, }, },];const edges = [ { id: nanoid(), source: nodeIds.start, target: nodeIds.process1, type: "animated", }, { id: nanoid(), source: nodeIds.process1, target: nodeIds.decision, type: "animated", }, { id: nanoid(), source: nodeIds.decision, target: nodeIds.output1, type: "animated", }, { id: nanoid(), source: nodeIds.decision, target: nodeIds.output2, type: "temporary", }, { id: nanoid(), source: nodeIds.output1, target: nodeIds.process2, type: "animated", }, { id: nanoid(), source: nodeIds.output2, target: nodeIds.process2, type: "temporary", },];const nodeTypes = { workflow: ({ data, }: { data: { label: string; description: string; handles: { target: boolean; source: boolean }; }; }) => ( <Node handles={data.handles}> <NodeHeader> <NodeTitle>{data.label}</NodeTitle> <NodeDescription>{data.description}</NodeDescription> </NodeHeader> <NodeContent> <p>test</p> </NodeContent> <NodeFooter> <p>test</p> </NodeFooter> </Node> ),};const edgeTypes = { animated: Edge.Animated, temporary: Edge.Temporary,};const Example = () => ( <Canvas edges={edges} edgeTypes={edgeTypes} fitView nodes={nodes} nodeTypes={nodeTypes} />);export default Example;Tutorial
Let's walk through how to build a workflow visualization using AI Elements. Our example will include custom nodes with headers, content, and footers, along with animated and temporary edge types.
Setup
First, set up a new Next.js repo and cd into it by running the following command (make sure you choose to use Tailwind in the project setup):
npx create-next-app@latest ai-workflow && cd ai-workflowRun the following command to install AI Elements. This will also set up shadcn/ui if you haven't already configured it:
npx ai-elements@latestNow, install the required dependencies:
npm i @xyflow/reactWe're now ready to start building our workflow!
Client
Let's build the workflow visualization step by step. We'll create the component structure, define our nodes and edges, and configure the canvas.
Import the components
First, import the necessary AI Elements components in your app/page.tsx:
'use client';
import { Canvas } from '@/components/ai-elements/canvas';
import { Connection } from '@/components/ai-elements/connection';
import { Controls } from '@/components/ai-elements/controls';
import { Edge } from '@/components/ai-elements/edge';
import {
Node,
NodeContent,
NodeDescription,
NodeFooter,
NodeHeader,
NodeTitle,
} from '@/components/ai-elements/node';
import { Panel } from '@/components/ai-elements/panel';
import { Toolbar } from '@/components/ai-elements/toolbar';
import { Button } from '@/components/ui/button';Define node IDs
Create a constant object to manage node identifiers. This makes it easier to reference nodes when creating edges:
const nodeIds = {
start: 'start',
process1: 'process1',
process2: 'process2',
decision: 'decision',
output1: 'output1',
output2: 'output2',
};Create mock nodes
Define the nodes array with position, type, and data for each node in your workflow:
const nodes = [
{
id: nodeIds.start,
type: 'workflow',
position: { x: 0, y: 0 },
data: {
label: 'Start',
description: 'Initialize workflow',
handles: { target: false, source: true },
content: 'Triggered by user action at 09:30 AM',
footer: 'Status: Ready',
},
},
{
id: nodeIds.process1,
type: 'workflow',
position: { x: 500, y: 0 },
data: {
label: 'Process Data',
description: 'Transform input',
handles: { target: true, source: true },
content: 'Validating 1,234 records and applying business rules',
footer: 'Duration: ~2.5s',
},
},
{
id: nodeIds.decision,
type: 'workflow',
position: { x: 1000, y: 0 },
data: {
label: 'Decision Point',
description: 'Route based on conditions',
handles: { target: true, source: true },
content: "Evaluating: data.status === 'valid' && data.score > 0.8",
footer: 'Confidence: 94%',
},
},
{
id: nodeIds.output1,
type: 'workflow',
position: { x: 1500, y: -300 },
data: {
label: 'Success Path',
description: 'Handle success case',
handles: { target: true, source: true },
content: '1,156 records passed validation (93.7%)',
footer: 'Next: Send to production',
},
},
{
id: nodeIds.output2,
type: 'workflow',
position: { x: 1500, y: 300 },
data: {
label: 'Error Path',
description: 'Handle error case',
handles: { target: true, source: true },
content: '78 records failed validation (6.3%)',
footer: 'Next: Queue for review',
},
},
{
id: nodeIds.process2,
type: 'workflow',
position: { x: 2000, y: 0 },
data: {
label: 'Complete',
description: 'Finalize workflow',
handles: { target: true, source: false },
content: 'All records processed and routed successfully',
footer: 'Total time: 4.2s',
},
},
];Create mock edges
Define the connections between nodes. Use animated for active paths and temporary for conditional or error paths:
const edges = [
{
id: 'edge1',
source: nodeIds.start,
target: nodeIds.process1,
type: 'animated',
},
{
id: 'edge2',
source: nodeIds.process1,
target: nodeIds.decision,
type: 'animated',
},
{
id: 'edge3',
source: nodeIds.decision,
target: nodeIds.output1,
type: 'animated',
},
{
id: 'edge4',
source: nodeIds.decision,
target: nodeIds.output2,
type: 'temporary',
},
{
id: 'edge5',
source: nodeIds.output1,
target: nodeIds.process2,
type: 'animated',
},
{
id: 'edge6',
source: nodeIds.output2,
target: nodeIds.process2,
type: 'temporary',
},
];Create the node types
Define custom node rendering using the compound Node components:
const nodeTypes = {
workflow: ({
data,
}: {
data: {
label: string;
description: string;
handles: { target: boolean; source: boolean };
content: string;
footer: string;
};
}) => (
<Node handles={data.handles}>
<NodeHeader>
<NodeTitle>{data.label}</NodeTitle>
<NodeDescription>{data.description}</NodeDescription>
</NodeHeader>
<NodeContent>
<p className="text-sm">{data.content}</p>
</NodeContent>
<NodeFooter>
<p className="text-muted-foreground text-xs">{data.footer}</p>
</NodeFooter>
<Toolbar>
<Button size="sm" variant="ghost">
Edit
</Button>
<Button size="sm" variant="ghost">
Delete
</Button>
</Toolbar>
</Node>
),
};Create the edge types
Map the edge type names to the Edge components:
const edgeTypes = {
animated: Edge.Animated,
temporary: Edge.Temporary,
};Build the main component
Finally, create the main component that renders the Canvas with all nodes, edges, controls, and custom UI panels:
const App = () => (
<Canvas
edges={edges}
edgeTypes={edgeTypes}
fitView
nodes={nodes}
nodeTypes={nodeTypes}
connectionLineComponent={Connection}
>
<Controls />
<Panel position="top-left">
<Button size="sm" variant="secondary">
Export
</Button>
</Panel>
</Canvas>
);
export default App;Key Features
The workflow visualization demonstrates several powerful features:
- Custom Node Components: Each node uses the compound components (
NodeHeader,NodeTitle,NodeDescription,NodeContent,NodeFooter) for consistent, structured layouts. - Node Toolbars: The
Toolbarcomponent attaches contextual actions (like Edit and Delete buttons) to individual nodes, appearing when hovering or selecting them. - Handle Configuration: Nodes can have source and/or target handles, controlling which connections are possible.
- Multiple Edge Types: The
animatedtype shows active data flow, whiletemporaryindicates conditional or error paths. - Custom Connection Lines: The
Connectioncomponent provides styled bezier curves when dragging new connections between nodes. - Interactive Controls: The
Controlscomponent adds zoom in/out and fit view buttons with a modern, themed design. - Custom UI Panels: The
Panelcomponent allows you to position custom UI elements (like buttons, filters, or legends) anywhere on the canvas. - Automatic Layout: The
Canvascomponent auto-fits the view and provides pan/zoom controls out of the box.
You now have a working workflow visualization! Feel free to explore dynamic workflows by connecting this to AI-generated process flows, or extend it with interactive editing capabilities using React Flow's built-in features.