Workflow

An example of how to use the AI Elements to build a workflow visualization with interactive nodes and animated connections.

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-workflow

Run the following command to install AI Elements. This will also set up shadcn/ui if you haven't already configured it:

npx ai-elements@latest

Now, install the required dependencies:

npm i @xyflow/react

We'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, let's build the interface:

workflow.tsx
"use client";

import { Canvas } from "@/components/ai-elements/canvas";
import { Edge } from "@/components/ai-elements/edge";
import {
  Node,
  NodeContent,
  NodeDescription,
  NodeFooter,
  NodeHeader,
  NodeTitle,
} from "@/components/ai-elements/node";
import { nanoid } from "nanoid";

const nodeIds = {
  decision: nanoid(),
  output1: nanoid(),
  output2: nanoid(),
  process1: nanoid(),
  process2: nanoid(),
  start: nanoid(),
};

const nodes = [
  {
    data: {
      description: "Initialize workflow",
      handles: { source: true, target: false },
      label: "Start",
    },
    id: nodeIds.start,
    position: { x: 0, y: 0 },
    type: "workflow",
  },
  {
    data: {
      description: "Transform input",
      handles: { source: true, target: true },
      label: "Process Data",
    },
    id: nodeIds.process1,
    position: { x: 500, y: 0 },
    type: "workflow",
  },
  {
    data: {
      description: "Route based on conditions",
      handles: { source: true, target: true },
      label: "Decision Point",
    },
    id: nodeIds.decision,
    position: { x: 1000, y: 0 },
    type: "workflow",
  },
  {
    data: {
      description: "Handle success case",
      handles: { source: true, target: true },
      label: "Success Path",
    },
    id: nodeIds.output1,
    position: { x: 1500, y: -100 },
    type: "workflow",
  },
  {
    data: {
      description: "Handle error case",
      handles: { source: true, target: true },
      label: "Error Path",
    },
    id: nodeIds.output2,
    position: { x: 1500, y: 100 },
    type: "workflow",
  },
  {
    data: {
      description: "Finalize workflow",
      handles: { source: false, target: true },
      label: "Complete",
    },
    id: nodeIds.process2,
    position: { x: 2000, y: 0 },
    type: "workflow",
  },
];

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;

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 Toolbar component 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 animated type shows active data flow, while temporary indicates conditional or error paths.
  • Custom Connection Lines: The Connection component provides styled bezier curves when dragging new connections between nodes.
  • Interactive Controls: The Controls component adds zoom in/out and fit view buttons with a modern, themed design.
  • Custom UI Panels: The Panel component allows you to position custom UI elements (like buttons, filters, or legends) anywhere on the canvas.
  • Automatic Layout: The Canvas component 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.