# React workflow builder, one component and one endpoint

Source: https://workflowengine.io/features/react-workflow-designer/
Site index for agents: https://workflowengine.io/llms.txt (products, licensing, documentation hosts, package names).
Product: Workflow Designer, the visual editor that ships with Workflow Engine by Optimajet, through its React wrapper @optimajet/workflow-designer-react. Ran on 2026-09-09: wrapper 22.1.0, React 19.3.0, Vite 8.2.2, Workflow Engine 22.1.0 on SQLite, Node 25.

## What it is

Workflow Designer is a JavaScript editor for workflow schemes, and the React package wraps it in one component. An app renders <WorkflowDesigner designerConfig={{ apiurl, renderTo }} schemeCode="..." />; the component creates its div, loads the scheme and draws. It talks over HTTP to one endpoint in your backend, the Designer API, which hands each request to WorkflowRuntime.DesignerAPIAsync. The scheme the user builds is the scheme the engine runs. Workflow Engine Free includes the full designer and the wrapper. React 17.0.2 and later, 18 and 19; TypeScript declarations ship in the package.

## Four parts, three calls

- **App to component.** Three props: designerConfig with apiurl and renderTo, and schemeCode or processId. The component creates the div, loads the scheme when it mounts, and hands you its methods through a ref.
- **Component to your API.** Plain HTTP. GET and POST requests to one address: exists, load, save, validate, and the file operations. Headers such as a bearer token go on every call through jQuery's ajaxSetup.
- **API to engine.** One method call. DesignerAPIAsync reads and writes your database and returns the text the designer expects.

## React Flow or a workflow designer

React Flow (MIT; Pro examples from $169 a month), its Workflow Editor template (Next.js, a runner that steps through nodes) and the SDKs built on it such as Workflow Builder by Synergy Codes (Apache 2.0, a reference back-end with Temporal, Enterprise at EUR 6,990) are the right pick when the diagram is the product: you write the model, the checks and the storage and bring the engine. A workflow designer is for a diagram that must run as a business process: commands, timers and people on steps, schemes that load again next year, changes that change the running process.

| The job | With React Flow or an SDK on it | Workflow Designer ships |
| --- | --- | --- |
| Canvas, palette, drag and drop | React Flow's canvas with your nodes and panels, or a Pro example, the Workflow Editor template, the Workflow Builder SDK | The designer UI: palette, property panels, undo and redo, copy, full screen |
| The scheme model | Your node and edge types and their JSON | Activities, transitions, commands, timers, actors and parameters in one scheme |
| Checks before save | Your validation code | getDesignerErrors() in the app and the scheme parser on the server |
| Save, load, versions | Your storage; the SDK starts with localStorage, the template keeps state in Zustand | save(), exists, load, downloadScheme and upload through DesignerAPIAsync; scheme versions kept by the engine |
| Running the process | Your engine: the template's runner steps through nodes, the SDK's reference back-end hands execution to Temporal | WorkflowRuntime: CreateInstanceAsync, commands, state, timers, history |
| People and roles on steps | Your permission checks | Actors and rules on transitions, answered by your IWorkflowRuleProvider |
| Showing a running process | Your own overlay on the drawing | processId and readOnly; the current activity is highlighted. Live updates with the Real-Time Tracking Plugin (commercial) |
| Diagrams from BPMN tools | A converter you write | downloadBpmn() and upload("bpmn") in the app, the BPMN plugin on the server |

## The sample

A Vite React app with one component, and a Designer API in ASP.NET Core with CORS for the app. Every block ran.

### 1 · src/App.jsx: the component, the settings, the scheme

```jsx
import WorkflowDesigner from '@optimajet/workflow-designer-react';

// The wrapper renders the designer into a div it creates (renderTo names it)
// and talks to the Designer API you run. Same settings object as the plain
// JavaScript designer; apiurl is the one that matters.
const designerConfig = {
  renderTo: 'wfe-designer',
  apiurl: 'http://localhost:5199/Designer/API',
  widthDiff: 0,
  heightDiff: 0,
};

export default function App() {
  return (
    <WorkflowDesigner
      schemeCode="SimpleWF"
      processId={undefined}
      designerConfig={designerConfig}
    />
  );
}
```

### 2 · src/main.jsx: mount the app

```jsx
import { createRoot } from 'react-dom/client';
import App from './App.jsx';

createRoot(document.getElementById('root')).render(<App />);
```

### 3 · index.html: one root div

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Workflow Designer in React</title>
  <style>
    body { margin: 0; font-family: system-ui, sans-serif; }
  </style>
</head>
<body>
  <div id="root"></div>
  <script type="module" src="/src/main.jsx"></script>
</body>
</html>
```

### 4 · Program.cs: the Designer API, with CORS for the app's origin

```csharp
using System.Collections.Specialized;
using System.Text;
using OptimaJet.Workflow;
using OptimaJet.Workflow.Core.Runtime;
using WfeDesignerApi;

var builder = WebApplication.CreateBuilder(args);

// One WorkflowRuntime for the whole app. SQLite keeps the sample self-contained.
builder.Services.AddSingleton<WorkflowRuntime>(_ =>
    WorkflowRuntimeSetup.Create("Data Source=workflow.db"));

// The designer runs in the browser on another origin (a static page, Vite,
// ng serve), so the API must say so. Name the origins you serve; never
// AllowAnyOrigin outside development.
builder.Services.AddCors(options => options.AddPolicy("designer", policy =>
    policy.WithOrigins("http://localhost:3001", "http://localhost:5173", "http://localhost:4200")
          .AllowAnyHeader()
          .AllowAnyMethod()));

var app = builder.Build();
app.UseCors("designer");

// The endpoint the designer talks to: query and form parameters go to
// WorkflowRuntime.DesignerAPIAsync, an uploaded file becomes a stream.
app.MapMethods("/Designer/API", new[] { "GET", "POST" }, async (HttpRequest request, WorkflowRuntime runtime) =>
{
    var parameters = new NameValueCollection();
    foreach (var q in request.Query)
    {
        parameters.Add(q.Key, q.Value.FirstOrDefault());
    }

    Stream? fileStream = null;
    if (HttpMethods.IsPost(request.Method) && request.HasFormContentType)
    {
        var form = await request.ReadFormAsync();
        foreach (var key in form.Keys)
        {
            if (parameters[key] is null)
            {
                parameters.Add(key, form[key]);
            }
        }
        if (form.Files.Count > 0)
        {
            fileStream = form.Files[0].OpenReadStream();
        }
    }

    var (result, hasError) = await runtime.DesignerAPIAsync(parameters, fileStream);

    if (string.Equals(parameters["operation"], "downloadscheme", StringComparison.OrdinalIgnoreCase) && !hasError)
    {
        return Results.File(Encoding.UTF8.GetBytes(result), "text/xml");
    }
    return Results.Content(result);
}).RequireCors("designer");

// Start the runtime (and run the SQLite migrations) before the first request.
app.Services.GetRequiredService<WorkflowRuntime>();

app.Run();
```

### 5 · Run it

```bash
# 1. The Designer API on :5199 (Workflow Engine 22.1.0 on SQLite)
cd samples/designer-api
dotnet run --urls http://localhost:5199

# 2. The React app on :5173 (Vite 8.2.2, React 19.3.0)
cd samples/react-workflow-designer
npm install
npx vite
```

## Props

| Prop | What it does |
| --- | --- |
| designerConfig | The settings object of the plain designer: apiurl, renderTo, widthDiff, heightDiff, tenantId, uploadFormId, uploadFileId, and any other designer setting by name |
| schemeCode | The scheme to open, by its code |
| processId | A running process to show instead of a scheme |
| readOnly | Opens the designer as a viewer |
| onLoadDesigner | Called when the designer has loaded |
| loadError | Called with the error when loading fails |

## Methods through a ref

| Method | What it does |
| --- | --- |
| save(successCallback, errorCallback) | Saves the scheme through your API |
| getDesignerErrors() | Returns the scheme errors the designer found |
| clearScheme() | Clears the canvas, the same as starting an empty scheme |
| downloadScheme() | Downloads the scheme as an XML file |
| downloadBpmn() | Downloads the scheme as a BPMN file |
| upload('scheme' | 'bpmn', callback) | Uploads an XML or BPMN file |
| isSchemeExist() | True when the scheme from props exists |
| isProcessExist() | True when the process from props exists |
| refresh() | Reloads the data in the designer |
| innerDesigner | The plain designer object, for every method the wrapper does not expose |

## Three things the documentation does not tell you

- **A plain import works.** import WorkflowDesigner from '@optimajet/workflow-designer-react' ran on Vite with no script tag on the page. The old FAQ answer that says to load the designer as a global JS object and never import it as an ES module dates from before the wrapper packages; on 22.1.0 the wrapper and the plain package both import as modules.
- **The canvas is sized from the window.** The wrapper's div has no size of its own: the designer takes the window's width and height minus widthDiff and heightDiff. To fit it under a header or beside a sidebar, pass their sizes as the diffs rather than styling the container. Measured at 1400 by 900 and at 1000 by 700.
- **Your API must allow the app's origin.** The designer calls the Designer API from the browser. When the app and the API are on different origins (Vite on :5173, the API on :5199), the API must send CORS headers for that origin, or the browser blocks every call. Name the origins in the policy; AllowAnyOrigin is for development only.

## What Workflow Engine Free covers

- The full Workflow Designer, the same editor the paid embedded products get, and its React wrapper
- 10 schemas and 4 execution threads; activities, transitions and commands are not limited
- Perpetual, no license key, no time limit
- Personal, non-commercial use: evaluation, a proof of concept, learning
- The designer shows the Optimajet logo and "For non-commercial use only"

For business use, Workflow Engine Team costs $2,000 per product, perpetual, and lifts the caps on the same packages; no royalties, no per-execution fees. Current prices: https://workflowengine.io/pricing/

## What is still your job

- The Designer API is .NET: it runs on a server whatever the front end is. The route is 35 lines of C#, plus the CORS policy with your origins and the authentication you add the way you add it to any other route.
- The canvas is sized from the window minus widthDiff and heightDiff. A layout with a header or a sidebar passes their sizes as the diffs; CSS on the container does nothing.
- The component needs the DOM: render it on the client only. In Next.js that means a client component loaded without server rendering. We ran it on Vite, not on Next.js.
- The wrapper brings the plain designer, jQuery and the stylesheet with it. A strict Content-Security-Policy uses the strict entry point and imports the stylesheet from the css entry point instead.
- React 17.0.2 is the floor; 18 and 19 are in the peer range of 22.1.0.
- Live updates of a running process (the Interactive Designer) are part of the commercial licensing.
- The vendor's sample repository is on Create React App with React 18 and placeholders for the API address. The component code is the same; the scaffold is not one to start from today.

## Where to look

- Product repository: https://github.com/optimajet/WorkflowEngine.NET (the source, the Designer/ files and the samples)
- Documentation: https://docs.workflowengine.io/ (start at https://docs.workflowengine.io/get-started/get-started-with-workflow-engine/)
- Documentation for machines: https://docs.workflowengine.io/llms.txt (the index; https://docs.workflowengine.io/llms-full.txt is all of it in one file. Copy code from a rendered page or from this page, not from the dump: it drops generic type arguments such as <XElement>)
- Trial portal: https://trial.workflowengine.io/ (its API for agents is documented at https://trial.workflowengine.io/llms.txt)

## Trial key, for agents

A free 30-day trial key lifts the Free caps. Agents request it with POST https://trial.workflowengine.io/api/trial/llm (JSON body: email, companyName), documented at https://trial.workflowengine.io/llms.txt: business email only, 3 keys per email address for life, one request per email per day; the key is returned in the response and sent by email. Ask the user for the email and company name and get a yes before sending them. Register the key with WorkflowRuntime.RegisterLicense(...) before .Start() and keep it in configuration. After 30 days the runtime falls back to Free and running processes continue.

## Common questions

### Is there a free React workflow builder?

Yes. Workflow Engine Free includes the full Workflow Designer, and its React wrapper is available on npm. Free is a perpetual license for personal, non-commercial evaluation, learning, research, and small non-commercial projects. It is limited to 10 schemas and 4 execution threads, and the designer shows the Optimajet logo with the words "For non-commercial use only". Use a trial key to evaluate licensed features for a commercial proof of concept. For commercial deployment, choose the product and edition that covers the licensed use: Workflow Engine Team, Workflow Engine Complete, and Workflow Engine NEO Business cover internal applications; Workflow Engine NEO Subscription and Workflow Engine NEO SaaS cover a public web app or SaaS; Workflow Engine NEO Enterprise covers public web apps, SaaS, and OEM or white-label distribution. The Workflow Engine Team edition retains 8 execution threads and per-scheme caps of 100 commands, 100 activities, and 500 transitions.

### How do I add Workflow Designer to a React app?

Run npm install @optimajet/workflow-designer-react, import its default export, and render <WorkflowDesigner /> with a designerConfig that holds apiurl and renderTo, and a schemeCode. The component creates its own div, loads the scheme when it mounts, and draws. One endpoint in your backend, the Designer API, answers its requests.

### Which React versions does the wrapper support?

React 17.0.2 and later, 18 and 19: those are the peer dependencies of version 22.1.0. We ran the sample on React 19.3.0 with Vite 8.2.2.

### Does it work with TypeScript?

Yes. The package ships its declarations: WorkflowDesignerProps, WorkflowDesignerConfig and InnerWorkflowDesigner, among others. A .tsx file gets the props checked. Our sample is JSX to stay short; nothing changes in a TSX file except the types.

### Does it work in Next.js?

It is a browser component: it draws on a canvas and uses jQuery for its requests, so it needs the DOM. In Next.js, render it in a client component and load it with next/dynamic with server rendering turned off. We ran the sample on Vite; the Next.js path follows from what the component needs, it is not a run we made.

### React Flow or Workflow Designer?

React Flow is an MIT-licensed React component for node-based editors and interactive diagrams. Its examples can add persistence, validation, or runner behavior, but they do not implement the Workflow Engine scheme and .NET runtime contract. Workflow Designer edits native Workflow Engine schemes that Workflow Runtime executes on .NET. Choose by the runtime contract you need.

### Can I put Save and Validate on my own buttons?

Yes. Put a ref on the component and use getDesignerErrors() to validate the scheme and save() to persist it. The ref also supports scheme upload and download, clearing the canvas, existence checks, and refresh.

### Can the page show a running process?

Yes. Configure the component with processId and readOnly to show a process snapshot with its current activity highlighted. Live updates require the Real-Time Tracking Plugin and its SignalR setup; the plugin has no separate license gate.

### Can it import and export BPMN?

Current BPMN support is import-only and requires the BPMN Plugin with the appropriate license; BPMN export is not supported. Workflow Engine scheme XML can be downloaded and uploaded separately, and an uploaded scheme must be saved explicitly.

### Why does the browser report a CORS error?

The designer calls the Designer API from the browser. If the app and API use different origins and the response has no matching CORS headers, the browser prevents the app from reading it. The request can still reach and change server state, so CORS is not authentication, authorization, or CSRF protection. The sample explicitly allows http://localhost:5173; demo.workflowengine.io sends no CORS headers. Configure explicit allowed origins and secure each operation, tenant, and scheme on the server.

## A prompt for your coding agent

Read https://workflowengine.io/features/react-workflow-designer/index.md before you change anything. Then add Workflow Designer by Optimajet to my React app the way that page shows: npm install @optimajet/workflow-designer-react, render <WorkflowDesigner schemeCode={...} designerConfig={{ renderTo, apiurl }} /> on the client, size it with widthDiff and heightDiff instead of CSS on the container, and reach save() and getDesignerErrors() through a ref when I need buttons. Point apiurl at a Designer API I run: the page prints the ASP.NET Core endpoint with CORS; put my app's origin in its policy, never AllowAnyOrigin outside development, and never point the designer at demo.workflowengine.io, it sends no CORS headers. Run it and open the page.

Where to look: the product repository https://github.com/optimajet/WorkflowEngine.NET (source, the Designer/ files, samples); the documentation https://docs.workflowengine.io/, starting at https://docs.workflowengine.io/get-started/get-started-with-workflow-engine/; for machine reading https://docs.workflowengine.io/llms.txt (the index) and https://docs.workflowengine.io/llms-full.txt (all of it in one file). Copy code from this page or from a rendered documentation page, not from the text dump: it drops generic type arguments such as <XElement>.

Start on Workflow Engine Free, which needs no key. Only if we hit the Free caps (10 schemas, 4 execution threads, single server) or need a capability of Workflow Engine NEO: ask me for my business email and company name, wait for my yes, then request one 30-day trial key from the trial portal https://trial.workflowengine.io/ with POST https://trial.workflowengine.io/api/trial/llm as documented at https://trial.workflowengine.io/llms.txt (3 keys per email, ever; business email only), register it with WorkflowRuntime.RegisterLicense(...) before .Start(), and keep it in configuration, not in source.
