TinyFlow: A Visual Editor for LLM Workflows

How I built a drag-and-drop workflow editor for LLM agents using React Flow and PocketFlow — and what it taught me about visual programming, CLI tooling, and the gap between 'works in demo' and 'works in production'.

After building Flowneer — a code-first flow builder for LLM agents — the natural next question was: what if you didn’t have to write code at all?

TinyFlow is my attempt at a visual editor for agent workflows. You drag nodes onto a canvas, connect them, and get a runnable workflow — either through the web UI or a CLI.

What it is

TinyFlow is built on top of React Flow for the canvas and PocketFlow as the underlying runtime. The editor lets you:

  • Drag and drop node types (LLM calls, conditionals, loops, custom functions)
  • Connect nodes with wires that represent data flow
  • Register custom functions and use them as nodes
  • Build and bundle the workflow to ESM, CJS, or IIFE output
  • Run, validate, and inspect workflows from the CLI

The project also ships with built-in LLM function nodes that work with any OpenAI-compatible API, and an in-memory key-value store for sharing state across nodes.

Why I built it

I kept running into a specific situation: I’d prototype an agent workflow in code, and then need to hand it off to someone on a product team who wanted to tweak it without going through me. A code-first approach just wasn’t going to work for that.

Visual editors for this space exist — LangFlow, Flowise, n8n — but they all felt overbuilt for what I needed. I wanted something lightweight, easily embeddable, and with a sane bundle story so you could ship a workflow as a JS module.

The interesting problems

Making React Flow feel like a real editor

React Flow handles the low-level canvas, but turning it into a usable editor requires a lot of work on top. State management for nodes and edges, copy-paste, undo/redo, the sidebar for configuring selected nodes, keyboard shortcuts — none of that comes for free.

I built the state layer around Zustand, which ended up being a good fit. The store holds the current graph, selection state, and undo stack. Each operation (add node, connect, update config) is a pure state mutation that can be replayed for undo.

The bundle builder

I wanted to be able to take a workflow defined in the visual editor and produce a self-contained JavaScript module you could import anywhere. This meant writing a bundler pass that serializes the graph to a dependency-ordered list of function calls, wraps them in the appropriate module format, and tree-shakes out any built-in nodes you’re not using.

I used Rollup under the hood and wrote a custom plugin that treats each node in the graph as a virtual module. The output is a clean ES module with one exported run function.

CLI tooling

The CLI (tinyflow run, tinyflow build, tinyflow validate) runs workflows defined as JSON files. Getting the CLI to share the same runtime as the web editor — so a workflow that runs in the browser also runs via the CLI without modification — required careful separation of the runtime from the browser-specific rendering layer.

Bun made this significantly easier. Running TypeScript directly without a build step meant I could share types and utilities between the CLI and the web app without a complex monorepo setup.

Current status

TinyFlow is a work-in-progress. The visual editor is functional, the CLI works, and the bundle builder produces valid output. The npm package is not yet published — I want to stabilize the node format before committing to a public API.

If you’re interested in where this is going, watch the GitHub repo. I’d especially welcome feedback on the node format and the bundle output.