asl-viewer
v1.1.0
Published
React library for viewing AWS Step Functions workflows in the browser
Maintainers
Readme
ASL Viewer
A React library for visualizing AWS Step Functions workflows (Amazon States Language) in the browser. Built with TypeScript and based on the AWS Toolkit for VS Code.
📖 Live Examples
Check out our Storybook to see interactive examples and explore all available components and features.

Features
- 🎨 Visual Workflow Rendering - Display ASL workflows as interactive graphs
- 🌓 Theme Support - Light and dark themes built-in
- 📐 Layout Options - Support for Top-to-Bottom and Left-to-Right layouts
- ✅ ASL Validation - Comprehensive validation for ASL syntax and semantics
- 🔄 Auto Layout - Automatic graph layout with reactive updates
- 📱 Responsive - Works on different screen sizes
- 🖱️ Interactive - Click handlers for states and connections
- 🔍 Detailed Inspection - Rich state details panel with JSON viewing
- 🌐 Multiple Input Sources - Load from definition objects, URLs, or files
- 📄 YAML Support - Support for both JSON and YAML formats
- 🔧 Extensible - Easy to customize and extend
- 📚 TypeScript - Full TypeScript support with comprehensive types
Installation
npm install asl-vieweror with Yarn:
yarn add asl-viewerCSS Import
Important: You must import the CSS file for the component to display correctly.
ES Modules / Webpack
import "asl-viewer/dist/index.css";CommonJS
require("asl-viewer/dist/index.css");HTML (if using a CDN)
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/asl-viewer@latest/dist/index.css"
/>CSS-in-JS / Styled Components
If you're using CSS-in-JS libraries or have build tools that don't handle CSS imports automatically, make sure to include the CSS file in your build process or import it in your main application file.
Quick Start
Basic Usage with Definition Object
import React from "react";
import { WorkflowViewer } from "asl-viewer";
import "asl-viewer/dist/index.css"; // Required CSS import
const workflow = {
Comment: "A simple minimal example",
StartAt: "Hello",
States: {
Hello: {
Type: "Task",
Resource: "arn:aws:lambda:us-east-1:123456789012:function:HelloWorld",
End: true,
},
},
};
function App() {
return (
<WorkflowViewer
definition={workflow}
theme="light"
width={800}
height={600}
/>
);
}Loading from URL
import React from "react";
import { WorkflowViewer } from "asl-viewer";
import "asl-viewer/dist/index.css"; // Required CSS import
function App() {
return (
<WorkflowViewer
url="https://example.com/workflow.json"
theme="light"
width={800}
height={600}
onLoadStart={() => console.log("Loading...")}
onLoadEnd={() => console.log("Loaded!")}
onLoadError={(error) => console.error("Error:", error)}
/>
);
}Loading from File Upload
import React, { useState } from "react";
import { WorkflowViewer, FileUploader } from "asl-viewer";
import "asl-viewer/dist/index.css"; // Required CSS import
function App() {
const [selectedFile, setSelectedFile] = useState<File | null>(null);
return (
<div>
<FileUploader
onFileSelect={setSelectedFile}
theme={{
background: "white",
borderColor: "#ddd",
textColor: "#333",
infoColor: "#007acc",
}}
/>
{selectedFile && (
<WorkflowViewer
file={selectedFile}
theme="light"
width={800}
height={600}
/>
)}
</div>
);
}YAML Support
import React from "react";
import { WorkflowViewer } from "asl-viewer";
const yamlWorkflow = `
Comment: "A workflow in YAML format"
StartAt: "HelloWorld"
States:
HelloWorld:
Type: "Pass"
Result: "Hello from YAML!"
End: true
`;
function App() {
function App() {
return (
<WorkflowViewer
definition={yamlWorkflow}
theme="light"
width={800}
height={600}
/>
);
}Interactive Mode with Draggable Nodes
import React from "react";
import { WorkflowViewer } from "asl-viewer";
function App() {
return (
<WorkflowViewer
definition={workflow}
theme="light"
width={900}
height={700}
readonly={false}
isDraggable={true}
isSelectable={true}
isMultiSelect={true}
useMiniMap={true}
useControls={true}
onStateClick={(state) => {
console.log("State clicked:", state);
}}
/>
);
}Presentation Mode (Read-Only)
import React from "react";
import { WorkflowViewer } from "asl-viewer";
function App() {
return (
<WorkflowViewer
definition={workflow}
theme="light"
width={800}
height={600}
readonly={true}
useControls={false}
useMiniMap={false}
isDraggable={false}
isSelectable={false}
useZoom={false}
/>
);
}Enhanced Navigation with MiniMap
import React from "react";
import { WorkflowViewer } from "asl-viewer";
function App() {
return (
<WorkflowViewer
definition={complexWorkflow}
theme="dark"
width={1000}
height={800}
useMiniMap={true}
useControls={true}
useFitView={true}
useZoom={true}
/>
);
}Supported Formats
ASL Viewer supports multiple input formats and sources:
Input Sources
- Definition Object: Pass ASL definition directly as JavaScript object
- URL: Load from any accessible URL (supports CORS)
- File Upload: Upload local JSON or YAML files via file picker or drag & drop
File Formats
- JSON: Standard ASL format as used by AWS Step Functions
- YAML: Human-readable YAML format with same structure as JSON
Content Types
The library automatically detects format based on:
- File extension (
.json,.yaml,.yml) - MIME type (
application/json,application/yaml,text/yaml) - Content analysis (fallback parsing)
Example Files
# JSON format
{
"Comment": "A simple workflow",
"StartAt": "FirstState",
"States": {
"FirstState": {
"Type": "Pass",
"End": true
}
}
}
# YAML format
Comment: "A simple workflow"
StartAt: "FirstState"
States:
FirstState:
Type: "Pass"
End: trueAPI Reference
WorkflowViewer Props
| Prop | Type | Default | Description |
| ------------------- | ---------------------------------- | --------- | ------------------------------------------- |
| definition | ASLDefinition \| string | - | The ASL workflow definition (JSON or YAML) |
| url | string | - | URL to load the ASL definition from |
| file | File | - | File object containing the ASL definition |
| theme | 'light' \| 'dark' | 'light' | Visual theme |
| layoutDirection | 'TB' \| 'LR' | 'TB' | Layout direction (Top-Bottom or Left-Right) |
| width | number | 800 | Viewer width in pixels |
| height | number | 600 | Viewer height in pixels |
| readonly | boolean | true | Whether the viewer is read-only |
| isConnectable | boolean | true | Whether nodes can be connected |
| isDraggable | boolean | false | Whether nodes can be dragged |
| isSelectable | boolean | true | Whether nodes can be selected |
| isMultiSelect | boolean | false | Whether multiple nodes can be selected |
| useMiniMap | boolean | false | Whether to show navigation minimap |
| useControls | boolean | true | Whether to show zoom/pan controls |
| useZoom | boolean | true | Whether zooming is enabled |
| useFitView | boolean | true | Whether to auto-fit view to show all nodes |
| showToolbar | boolean | false | Whether to show the toolbar |
| hideComment | boolean | false | Whether to hide the workflow comment |
| onStateClick | (state: StateNode) => void | - | Callback when a state is clicked |
| onValidationError | (error: ValidationError) => void | - | Callback for validation errors |
| onLoadStart | () => void | - | Callback when loading starts |
| onLoadEnd | () => void | - | Callback when loading completes |
| onLoadError | (error: Error) => void | - | Callback when loading fails |
| className | string | - | Additional CSS class names |
| style | React.CSSProperties | - | Inline styles for the root container |
Note: You must provide exactly one of definition, url, or file.
Usage Modes
Interactive Mode
Enable full interactivity with draggable nodes, multi-selection, and comprehensive controls:
<WorkflowViewer
definition={workflow}
readonly={false}
isDraggable={true}
isSelectable={true}
isMultiSelect={true}
useMiniMap={true}
useControls={true}
onStateClick={(state) => console.log("Clicked:", state)}
/>Presentation Mode
Perfect for presentations or documentation with minimal UI:
<WorkflowViewer
definition={workflow}
readonly={true}
useControls={false}
useMiniMap={false}
isDraggable={false}
isSelectable={false}
useZoom={false}
/>Navigation Mode
Ideal for complex workflows with enhanced navigation features:
<WorkflowViewer
definition={largeWorkflow}
useMiniMap={true}
useControls={true}
useFitView={true}
useZoom={true}
width={1000}
height={800}
/>Horizontal Layout
Display the workflow from left to right instead of top to bottom:
<WorkflowViewer
definition={workflow}
layoutDirection="LR"
width={1000}
height={600}
/>Embedded Mode
Compact view for embedding in dashboards or smaller spaces:
<WorkflowViewer
definition={simpleWorkflow}
width={400}
height={300}
useControls={false}
useMiniMap={false}
useFitView={true}
/>FileUploader Props
| Prop | Type | Default | Description |
| -------------- | ---------------------- | -------------------- | -------------------------------- |
| onFileSelect | (file: File) => void | required | Callback when a file is selected |
| theme | ViewerTheme | required | Theme object for styling |
| accept | string | ".json,.yaml,.yml" | File types to accept |
| disabled | boolean | false | Whether the uploader is disabled |
| className | string | - | Additional CSS class names |
| style | React.CSSProperties | - | Inline styles |
URLInput Props
| Prop | Type | Default | Description |
| ------------- | ----------------------- | ---------------------------------- | ------------------------------ |
| onUrlSubmit | (url: string) => void | required | Callback when URL is submitted |
| theme | ViewerTheme | required | Theme object for styling |
| disabled | boolean | false | Whether the input is disabled |
| placeholder | string | "Enter URL to ASL definition..." | Placeholder text |
| className | string | - | Additional CSS class names |
| style | React.CSSProperties | - | Inline styles |
Types
import type {
ASLDefinition,
StateDefinition,
StateType,
ValidationError,
WorkflowViewerProps,
ViewerTheme,
StateNode,
} from "asl-viewer";Utilities
import {
validateASLDefinition,
parseASLDefinition,
createGraphLayout,
loadFromURL,
loadFromFile,
parseDefinitionString,
} from "asl-viewer";
// Validate an ASL definition
const errors = validateASLDefinition(workflow);
// Load from URL
const workflowFromUrl = await loadFromURL("https://example.com/workflow.json");
// Load from file
const workflowFromFile = await loadFromFile(file);
// Parse string (JSON or YAML)
const workflowFromString = parseDefinitionString(yamlOrJsonString);
// Parse and get structured data
const parsed = parseASLDefinition(workflow);
// Create custom layout
const layout = createGraphLayout(parsed.nodes, parsed.connections);Supported ASL Features
- ✅ Task States - Lambda functions, activities, and other tasks
- ✅ Choice States - Conditional branching with choice rules
- ✅ Pass States - Data transformation and flow control
- ✅ Wait States - Delays and timeouts
- ✅ Succeed/Fail States - Terminal states
- ✅ Parallel States - Concurrent execution branches
- ✅ Map States - Iteration over arrays (basic support)
- ✅ Retry/Catch - Error handling configuration
- ✅ Input/Output Processing - Path expressions and filters
Feature Configuration
Interactive Features
Control user interactions with the workflow:
// Enable all interactive features
<WorkflowViewer
definition={workflow}
readonly={false}
isDraggable={true} // Drag nodes around
isSelectable={true} // Click to select nodes
isMultiSelect={true} // Select multiple nodes
isConnectable={true} // Connect nodes (if applicable)
/>
// Read-only with selection only
<WorkflowViewer
definition={workflow}
readonly={true}
isSelectable={true}
isDraggable={false}
isMultiSelect={false}
/>Navigation Controls
Configure zoom, pan, and navigation features:
// Full navigation controls
<WorkflowViewer
definition={workflow}
useControls={true} // Show zoom/pan buttons
useZoom={true} // Enable zoom functionality
useFitView={true} // Auto-fit content to view
useMiniMap={true} // Show minimap for navigation
/>
// Minimal navigation
<WorkflowViewer
definition={workflow}
useControls={false}
useZoom={false}
useFitView={true}
useMiniMap={false}
/>Event Handling
Handle user interactions and loading events:
<WorkflowViewer
definition={workflow}
onStateClick={(state) => {
console.log("State clicked:", state.name, state.type);
// Handle state selection, show details, etc.
}}
onValidationError={(error) => {
console.error("Validation error:", error.message);
// Handle validation errors
}}
onLoadStart={() => {
console.log("Loading workflow...");
// Show loading indicator
}}
onLoadEnd={() => {
console.log("Workflow loaded successfully");
// Hide loading indicator
}}
onLoadError={(error) => {
console.error("Failed to load workflow:", error);
// Show error message
}}
/>Examples
Check out the /examples directory for complete usage examples:
Development
Setup
git clone <repository>
cd asl-viewer
yarn installBuild
yarn buildStorybook
Run Storybook locally for development:
yarn storybookBuild Storybook for production:
yarn build-storybookThe Storybook is automatically deployed to GitHub Pages at https://cleissonb.github.io/asl-viewer/ when changes are pushed to the main branch.
Testing
yarn testCustomization
Custom Themes
Example 1:
import { WorkflowViewer, getTheme } from "asl-viewer";
import "asl-viewer/dist/index.css"; // Don't forget the CSS import
const customTheme = {
...getTheme("light"),
nodeColors: {
...getTheme("light").nodeColors,
taskState: "#ff6b6b",
choiceState: "#4ecdc4",
},
};
<WorkflowViewer definition={workflow} theme={customTheme} />;Example 2:
import { WorkflowViewer, createCustomTheme } from "asl-viewer";
import "asl-viewer/dist/index.css"; // Don't forget the CSS import
const customPurple = createCustomTheme("dark", {
name: "customPurple",
background: "#1a0033",
surfaceColor: "#2d1b69",
nodeColors: {
task: "#4c1d95",
choice: "#7c2d12",
succeed: "#065f46",
},
nodeBorderColors: {
task: "#8b5cf6",
choice: "#f59e0b",
succeed: "#10b981",
},
textColor: "#e879f9",
connectionColor: "#c084fc",
tooltipBackground: "#3c004d",
tooltipTextColor: "#f0f0f0",
});
<WorkflowViewer definition={workflow} theme={customPurple} />;Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feature-name - Make your changes
- Add tests for new functionality
- Run the test suite:
yarn test - Submit a pull request
License
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
