Motia Icon
Workbench

UI Steps

UI Steps provide a powerful way to create custom, visually appealing representations of your workflow steps in the Workbench flow visualization tool.

With UI Steps, you can enhance the user experience by designing intuitive, context-aware visual components that clearly communicate your flow's sequencing and events.

Overview

To create a custom UI for a step, create a .tsx or .jsx file next to your step file with the same base name:

steps/ 
└── myStep/ 
  ├── myStep.step.ts      # Step definition
  └── myStep.step.tsx     # Visual override

Basic Usage

Let's override an EventNode but keeping the same look. Like the image below. We're going to add an image on the side and show the description.

Custom Event Node

// myStep.step.tsx
 
import { EventNode, EventNodeProps } from 'motia/workbench'
import React from 'react'
 
export const Node: React.FC<EventNodeProps> = (props) => {
  return (
    <EventNode {...props}>
      <div className="flex flex-row items-start gap-2">
        <div className="text-sm text-gray-400 font-mono">{props.data.description}</div>
        <img
          style={{ width: '64px', height: '64px' }}
          src="https://www.motia.dev/icon.png"
        />
      </div>
    </EventNode>
  )
}

Components

Motia Workbench provides out of the box components that you can use to create custom UI steps, which apply to different types of steps.

ComponentProps TypeDescription
EventNodeEventNodePropsBase component for Event Steps, with built-in styling and connection points
ApiNodeApiNodePropsComponent for API Steps, includes request/response visualization capabilities
CronNodeCronNodePropsBase component for Cron Steps, displays timing information
NoopNodeNoopNodePropsBase component for NoopNodes with a different color to comply workbench legend

Customizing completely

You can also fully customize your node making it look completely different from the result. Let's draw the following node.

Custom Ideator Agent Node

import { BaseHandle, EventNodeProps, Position } from 'motia/workbench'
import React from 'react'
 
export const Node: React.FC<EventNodeProps> = (props) => {
  return (
    <div className="w-80 bg-black text-white rounded-xl p-4">
      <div className="group relative">
        <BaseHandle type="target" position={Position.Top} variant="event" />
 
        <div className="flex items-center space-x-3">
          <img className="w-8 h-8" src="https://cdn-icons-png.flaticon.com/512/12222/12222588.png" />
          <div className="text-lg font-semibold">{props.data.name}</div>
        </div>
 
        <div className="mt-2 text-sm font-medium text-gray-300">{props.data.description}</div>
 
        <div className="mt-3 flex flex-col gap-2 border border-gray-800 border-solid p-2 rounded-md w-full">
          <div className="flex items-center text-xs text-gray-400 space-x-2">Input</div>
          <div className="flex flex-col gap-2 whitespace-pre-wrap font-mono">
            <div className="flex items-center gap-2">
              <div className="">contentIdea:</div>
              <div className="text-orange-500">string</div>
            </div>
            <div className="flex items-center gap-2">
              <div className="">contentType:</div>
              <div className="text-orange-500">string</div>
            </div>
          </div>
        </div>
 
        <div className="mt-3 flex flex-col gap-2 border border-gray-800 border-solid p-2 rounded-md w-full">
          <div className="flex items-center text-xs text-gray-400 space-x-2">Output</div>
          <div className="flex flex-col gap-2 whitespace-pre-wrap font-mono">
            <div className="flex items-center gap-2">
              <div className="">topic:</div>
              <div className="text-orange-500">string</div>
            </div>
            <div className="flex items-center gap-2">
              <div className="">subtopics:</div>
              <div className="text-orange-500">string[]</div>
            </div>
            <div className="flex items-center gap-2">
              <div className="">keywords:</div>
              <div className="text-orange-500">string[]</div>
            </div>
            <div className="flex items-center gap-2">
              <div className="">tone:</div>
              <div className="text-orange-500">string</div>
            </div>
            <div className="flex items-center gap-2">
              <div className="">audience:</div>
              <div className="text-orange-500">string</div>
            </div>
          </div>
        </div>
 
        <BaseHandle type="source" position={Position.Bottom} variant="event" />
      </div>
    </div>
  )
}

Important Notes

  • You will need to add <BaseHandle> to your node, otherwize it won't show the connectors.
  • If your node has padding, make sure to add a group inside your node with class group relative so the handles can be correctly positioned.
Feel free to create your own custom components and reuse across multiple notes.

Styling Guidelines

GuidelineDescription
Use Tailwind's utility classes onlyStick to Tailwind CSS utilities for consistent styling
Avoid arbitrary valuesUse predefined scales from the design system
Keep components responsiveEnsure UI elements adapt well to different screen sizes
Follow Motia's design systemMaintain consistency with Motia's established design patterns

Best Practices

PracticeDescription
Use base componentsUse EventNode and ApiNode when possible
Keep it simpleMaintain simple and clear visualizations
Optimize performanceMinimize state and computations
DocumentationDocument custom components and patterns
Style sharingShare common styles through utility classes
Need help? See our Community Resources for questions, examples, and discussions.

On this page