workflowengineby Optimajet · since 2014

v22.1.0 · Blazor Server and WebAssembly · npm @optimajet/workflow-designer · free tier included

Blazor workflow designer, embedded through JavaScript interop

Workflow Engine by Optimajet ships with Workflow Designer, a visual editor for workflow schemes. It is a JavaScript component, so a Blazor page can host it with one IJSRuntime call. You get the full designer in Workflow Engine Free, and the scheme you draw is the scheme the engine runs. This page has the whole sample, tested on .NET 8, and the three things that trip people up.

Further down this page: how the four parts talk to each other, the sample files, the three gotchas, and a prompt that lets your AI agent do the integration.

Workflow Engine HTML5 designer showing the Vacation Approval scheme: Activities (Vacation request created, Manager signing, BigBoss signing, Accounting review, Request approved) connected by Approve and Reject Transitions, a SendToBigBoss timer transition, with Timers, Parameters, and Code actions panels

One JavaScript call putsWorkflow Designer on your Blazor page

Workflow Designer is the editor that ships with Workflow Engine. It is written in JavaScript and it runs in the browser. Blazor cannot render it as a Razor component, and it does not need to. A Blazor page renders one empty div, waits for the first render, and calls one JavaScript function through IJSRuntime. That function creates the designer inside the div.

From then on the designer does not talk to Blazor at all. It talks to a small endpoint in your app, the Designer API, to load, save and check schemes. Behind that endpoint sits WorkflowRuntime, the engine that runs the scheme later. So the diagram your user draws is the process your app executes.

Your Blazor page

One empty div and one call in OnAfterRenderAsync. The page is 21 lines with the template, and the same page works in Blazor Server and Blazor WebAssembly.

Initial activity

The designer, in the browser

The same @optimajet/workflow-designer package React and Angular use, version 22.1.0, loaded from a CDN or from npm. No Blazor build of it, no fork.

Your Designer API

One endpoint in your app. It hands each request to WorkflowRuntime.DesignerAPIAsync and returns the answer. About 30 lines.

The engine, on your server

WorkflowRuntime saves the scheme and runs the process later. The sample uses SQLite; in production you pick one of six databases.

Free to start

Workflow Engine Free includes the full designer: 10 schemas, 4 execution threads, no time limit, no license key.

The same scheme runs

What the user draws is what the engine executes. Change the diagram, and the process changes with it. There is no export step.

Current activity

Four parts, three calls

Only the first call is Blazor-specific. The other two are the same in a React app, an Angular app, or a plain HTML page, which is why the designer has one set of documentation for all of them.

InvokeVoidAsyncGET and POSTDesignerAPIAsyncBlazor pageyour Razor componentWorkflow DesignerJavaScript, in the browserDesigner APIone endpoint in your appWorkflowRuntimeyour server and database
01

Blazor to designer

JavaScript interop. One call on the first render, with the element id, the API address and the scheme code.

02

Designer to your app

Plain HTTP. The designer sends GET and POST requests to one address: exists, load, save, validate, and the file operations.

03

Endpoint to engine

One method call. DesignerAPIAsync reads and writes your database and returns the text the designer expects.

Diagram library or workflow designer?

Search for a Blazor workflow designer and most answers point at diagram libraries: Blazor.Diagrams (MIT), the Syncfusion and Telerik diagram components. They are good at drawing. The question is what happens after the drawing.

If you only need pictures, a diagram library is the right pick. It is smaller, it is not tied to any engine, and you shape it as you like. A few node types and no execution: a library wins, and this page is not for you.

You need a workflow designer when the diagram must run. Steps get commands, timers and people. A scheme saved this year must load next year. A change to the diagram must change the running process. At that point the drawing is the small part. The scheme model, its checks, its storage and the runtime are the large part, and a diagram library gives you none of them. Elsa takes the other route and ships Elsa Studio, a separate Blazor application with its own server and login; here the designer is a component on your page.

The jobWith a diagram libraryWorkflow Designer ships
Canvas, palette, drag and dropYour node and link components on the library canvasThe designer UI: palette, property panels, undo and redo, copy, full screen
The scheme modelYour own classes for steps, links and rules, and their JSONActivities, transitions, commands, timers, actors and parameters in one scheme
Checks before saveYour validation codedesigner.validate() in the browser and the scheme parser on the server
Save, load, versionsYour storage, your versioningexists, load, save, downloadscheme and uploadscheme through DesignerAPIAsync; scheme versions kept by the engine
Running the processYour engineWorkflowRuntime: CreateInstanceAsync, commands, state, timers, history
People and roles on stepsYour permission checksActors and rules on transitions, answered by your IWorkflowRuleProvider
Showing a running processYour own overlay on the drawingPass processId; the current activity is highlighted. Live updates with the Real-Time Tracking Plugin (commercial)
Diagrams from BPMN toolsA converter you writeBPMN 2.0 import through the BPMN plugin

The designer object and every method in the table are described on the Workflow Designer feature page; BPMN import has its own page.

The whole sample, four files

Two NuGet packages, four tags in App.razor, and four files. About 140 lines in all, and most of them are the template's own. It ran on .NET 8 SDK 8.0.421 with the Blazor Web App template, Workflow Engine 22.1.0 on SQLite, and designer 22.1.0 from jsDelivr.

1 · Create the app and add the two packagesbash
dotnet new blazor -n WfeBlazorDesigner --interactivity Server --empty
cd WfeBlazorDesigner
dotnet add package WorkflowEngine.NETCore-Core --version 22.1.0
dotnet add package WorkflowEngine.NETCore-ProviderForSQLite --version 22.1.0
2 · Components/App.razor: one stylesheet, three scripts, in this orderhtml
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <base href="/" />
    <link rel="stylesheet" href="app.css" />
    <link rel="stylesheet" href="WfeBlazorDesigner.styles.css" />
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@@optimajet/workflow-designer@22/dist/workflowdesigner.min.css" />
    <HeadOutlet />
</head>

<body>
    <Routes />
    <script src="_framework/blazor.web.js"></script>
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@@optimajet/workflow-designer@22/dist/workflowdesignerfull.min.js"></script>
    <script src="js/wfe-designer.js"></script>
</body>

</html>
3 · Components/Pages/Designer.razor: the pagerazor
@page "/designer"
@rendermode InteractiveServer
@inject IJSRuntime JS

<PageTitle>Workflow Designer in Blazor</PageTitle>

<div id="wfe-designer" style="height: 720px;"></div>

@code {
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (!firstRender) return;

        await JS.InvokeVoidAsync("wfeDesigner.render", new
        {
            apiUrl = "/Designer/API",
            elementId = "wfe-designer",
            schemeCode = "SimpleWF"
        });
    }
}
4 · wwwroot/js/wfe-designer.js: the bridgejavascript
// Bridge between the Blazor page and the Workflow Designer global object.
window.wfeDesigner = {
  render(options) {
    const host = document.getElementById(options.elementId);
    const designer = new WorkflowDesigner({
      apiurl: options.apiUrl,
      renderTo: options.elementId,
      graphwidth: host.clientWidth,
      graphheight: host.clientHeight,
      showSaveButton: true,
    });
    const data = { schemecode: options.schemeCode, processid: undefined };
    if (designer.exists(data)) {
      designer.load(data);
    } else {
      designer.create(data.schemecode);
    }
  },
};
5 · WorkflowRuntimeSetup.cs: the engine on SQLite, from the vendor's own install pagecsharp
using System.Xml.Linq;
using OptimaJet.Workflow.Core.Builder;
using OptimaJet.Workflow.Core.Parser;
using OptimaJet.Workflow.Core.Runtime;
using OptimaJet.Workflow.Migrator;
using OptimaJet.Workflow.SQLite;

namespace WfeBlazorDesigner;

public static class WorkflowRuntimeSetup
{
    public static WorkflowRuntime Create(string connectionString)
    {
        var provider = new SqliteProvider(connectionString);

        var workflowBuilder = new WorkflowBuilder<XElement>(
            provider,
            new XmlWorkflowParser(),
            provider
        ).WithDefaultCache();

        return new WorkflowRuntime()
            .WithBuilder(workflowBuilder)
            .WithPersistenceProvider(provider)
            .RunMigrations()
            .Start();
    }
}
6 · Program.cs: register the runtime and expose the Designer APIcsharp
using System.Collections.Specialized;
using System.Text;
using OptimaJet.Workflow;
using OptimaJet.Workflow.Core.Runtime;
using WfeBlazorDesigner;
using WfeBlazorDesigner.Components;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorComponents().AddInteractiveServerComponents();

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

var app = builder.Build();
app.UseStaticFiles();
app.UseAntiforgery();

// The endpoint the designer talks to. Same origin as the page, so no CORS setup.
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);
}).DisableAntiforgery();

app.MapRazorComponents<App>().AddInteractiveServerRenderMode();

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

app.Run();

The endpoint copies the shape of the DesignerController in Optimajet's integration guide into a minimal API handler: query and form parameters go into one collection, an uploaded file becomes a stream, and DesignerAPIAsync answers. The designer posts plain forms with no antiforgery token, so the endpoint opts out of that check. Add authentication to it the way you protect any other route.

Three things the documentation does not tell you

Write @@optimajet in .razor files

In a .razor file the @ sign starts C#. The CDN path @optimajet/workflow-designer needs a double @@, or the build fails with "The name optimajet does not exist in the current context".

Use an interactive render mode

Static server-side rendering has no JavaScript interop. Put @rendermode InteractiveServer (or InteractiveWebAssembly) on the page, and make the call in OnAfterRenderAsync on the first render. During prerendering the call is not possible.

Keep the Designer API on the same origin

The designer calls the API from the browser. demo.workflowengine.io sends no CORS headers, so a local Blazor app pointed at it fails with a CORS error. Put the endpoint in your app, as the sample does, or add CORS headers to your API.

Optimajet's older sample, workflow-designer-blazor-sample on GitHub, targets the .NET 6 Blazor Server template (_Host.cshtml), designer 12.5.1, and the demo API. Read it for the idea; use the files above for a current app. Two folders of the main repository are useful next to it: the Designer folder holds workflowdesigner.min.js, the CSS, the templates and the localization files, for apps that cannot load from a CDN, and the ASP.NET Core samples show a full app with the classic DesignerController for SQL Server, PostgreSQL, MySQL, Oracle and MongoDB. They were assembled for .NET Core 2.1, so read them for the structure, not the project files.

Do it with your AI agent

Paste one prompt into Cursor, Claude Code, Copilot, or the chat you use. The agent reads this page as Markdown, adds the four files to your Blazor Web App, and asks for a trial key only when you need one, and only after you say yes.

The prompt
Read https://workflowengine.io/features/blazor-workflow-designer/index.md before you change anything. Then add Workflow Designer by Optimajet to my Blazor Web App the way that page shows: the two NuGet packages; the stylesheet and the three scripts in App.razor, after blazor.web.js, written as @@optimajet in .razor files; the Designer page with @rendermode InteractiveServer; the JS bridge; the runtime setup on SQLite; and the /Designer/API endpoint on the same origin as the page. Do not point the designer at demo.workflowengine.io, it sends no CORS headers. Run the app and open /designer.

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.

What the agent gets

This page as Markdown: the four files, the three gotchas, the licence facts, the trial API and the links an agent needs (the GitHub repository, the documentation and its llms files, the trial portal), without the layout. Cursor, Claude Code, Copilot and the web chats all read it the same way.

What it asks you for

Nothing for Workflow Engine Free. For a trial key, your business email and company name, and a yes before it sends them. The portal gives 3 keys per email address, ever, so the prompt says to request once.

Where the key goes

WorkflowRuntime.RegisterLicense(...) before .Start(), read from configuration rather than source. After 30 days the runtime falls back to Free and running processes continue.

The trial API and its limits are documented by Optimajet at trial.workflowengine.io/llms.txt and on the license key page. Keys are for business email addresses; personal and disposable domains are refused.

Blazor Server today, WebAssembly the same way

We ran the sample as a Blazor Web App with the InteractiveServer render mode. Optimajet documents the same path for Blazor WebAssembly: the scripts go into wwwroot/index.html and the page calls the same function. One thing does not change with the hosting model. The engine is a .NET library that needs a database, so the Designer API always lives on a server. In WebAssembly the browser calls that server over HTTP, the same way the designer does anyway.

Blazor ServerBlazor WebAssembly
Where the scripts goComponents/App.razorwwwroot/index.html
Render mode@rendermode InteractiveServer@rendermode InteractiveWebAssembly, or a standalone WebAssembly app
Where the Designer API runsIn the same appIn your API project on a server; the browser calls it over HTTP
The callIJSRuntime.InvokeVoidAsync in OnAfterRenderAsyncThe same
StatusRan on .NET 8 SDK 8.0.421Documented by Optimajet; not run for this page

Where Blazor developers use it

Four situations that come up in the Blazor questions we read before writing this page. Each one is the same four files with a different scheme behind them.

An admin page for approval flows

Your Blazor back office gets a Designer page. The team draws the approval steps, and the engine runs them. A change to the flow is an edit on the canvas, not a ticket.

A product where each customer has its own process

Load a scheme by code per customer. The designer is the same for everyone; the schemes differ. One deployment, many processes.

Replacing a hand-built flowchart editor

Teams that started with a diagram library and now need commands, timers and roles move the drawing into the designer and keep the engine underneath it.

Support staff watching a request move

Pass a processId instead of a schemeCode. The current step is highlighted on the same diagram the analyst drew, so support sees where a request is stuck.

What Free covers, and where Team starts

If you are a developer who needs a workflow designer in Blazor, start with Workflow Engine Free. It has no cut-down editor. It covers the evaluation, the proof of concept and the learning, which is most of the way to a decision.

  • The full Workflow Designer, the same editor the paid embedded products get
  • 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"

When the app goes to work for a business, you need a commercial edition. Workflow Engine Team costs $2,000 per product, perpetual, and lifts the caps on the same packages. There are no royalties and no per-execution fees. The full table, with the editions of Workflow Engine NEO for multi-tenant SaaS, is on the pricing page, and the free product has its own page.

What is still your job

Seven things to know before you plan the work. None of them is hidden in the sample; they are just easy to miss.

  • jQuery 3.7.1 loads before the designer. If your app does not use jQuery, this is one extra script.
  • There is no Blazor package. You keep a small JavaScript file (19 lines in the sample) and call it through IJSRuntime.
  • The Designer API is your endpoint, about 30 lines, and you add authentication to it like to any other route.
  • The page must use an interactive render mode. Static server-side rendering cannot host the designer.
  • Free runs in single-server mode with 10 schemas and 4 execution threads, and shows the non-commercial badge.
  • Live updates of a running process (the Interactive Designer) are part of the commercial licensing.
  • We ran Blazor Server on .NET 8. WebAssembly follows the vendor documentation; we did not run it.

Common questions

Answers to common questions about embedding Workflow Designer in a Blazor application.

  1. Is there a Blazor component for Workflow Designer?

    Workflow Designer is not shipped as a native Razor component or Blazor-specific NuGet package. It is a JavaScript component. In the Interactive Server sample on this page, a Razor component renders an empty div and initializes the designer through IJSRuntime in OnAfterRenderAsync. Other Blazor hosting models need their own JavaScript initialization and hosting setup.

  2. Is the Blazor workflow designer free?

    Yes. Workflow Engine Free includes the full Workflow Designer, the same editor the paid embedded products get. 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.

  3. Do I need jQuery?

    Yes. Load a compatible jQuery version before the Designer bundle. This sample was verified with jQuery 3.7.1; that is the tested version, not a requirement to use that exact release. In a Blazor Web App the order in App.razor is blazor.web.js, then jQuery, then the designer, then your small bridge file.

  4. Why does the designer fail with a CORS error?

    The designer calls the Designer API from the browser. If that API is on another origin and its response has no matching CORS headers, the browser prevents the page from reading the response. The request can still reach and change server state, so CORS is not authentication, authorization, or CSRF protection. demo.workflowengine.io/Designer/API sends no CORS headers, so a Blazor app on localhost cannot use it as a cross-origin browser API. Put the endpoint on the same origin, as this sample does, or configure explicit allowed origins, and secure the endpoint separately.

  5. Does it work in Blazor WebAssembly?

    Yes, but the hosting setup is different. The sample on this page is verified only for a .NET 8 Blazor Web App using Interactive Server. Interactive WebAssembly in a Blazor Web App requires a .Client project, WebAssembly service and render-mode registration, and client-side component placement. A standalone Blazor WebAssembly app instead uses wwwroot/index.html. In both cases the Designer API remains on a server.

  6. Which render mode does the page need?

    The IJSRuntime and OnAfterRenderAsync technique shown here requires an interactive render mode; this sample uses Interactive Server. A static-SSR page can still initialize the JavaScript Designer from a JS initializer, but it needs JS-side setup and enhanced-navigation cleanup instead of component JS interop. Interactive WebAssembly has separate host requirements.

  7. Can I use it as a Blazor flowchart component?

    It draws one kind of flowchart: a workflow scheme with activities, transitions, commands, timers and actors. If you need free-form diagrams such as org charts or mind maps, a diagram library is the better tool. If the diagram must run as a process, the designer gives you the drawing and the engine that runs it.

  8. Where are the schemes saved?

    Through the configured scheme persistence provider. This sample uses SQLite, and the built-in database providers store schemes in the selected database. Workflow Engine Core can also use another scheme-persistence implementation. The saved source scheme is used to build executable scheme versions for WorkflowRuntime.

  9. Can the Blazor page show a running process?

    Not with the bridge exactly as shown. Extend it to map processId to the Designer processid option and set readonly to true. This displays a snapshot of the current process with its activity highlighted. Live updates additionally require the Real-Time Tracking Plugin, its mapped SignalR endpoint, and realTimeTrackingUrl. The plugin has no separate license gate.

  10. Which .NET version was this tested on?

    .NET 8 SDK 8.0.421 with the Blazor Web App template. The SQLite provider package targets .NET 8 and .NET 10, and the Core package targets netstandard2.0. Both packages were 22.1.0, the same version as the designer script.

Try it in an afternoon

Install Workflow Engine Free, add the four files, and open the page. When the designer shows your first scheme, you have the whole path from the canvas to a running process, on your own machine.

Optimajet's Integrate the Designer guide covers the React, Angular and vanilla JavaScript paths next to the Blazor one, if your product has more than one front end. The same designer on a plain page, from one script tag or one npm import, is on the JavaScript workflow designer page, and as one component in a React app on the React workflow designer page.