Skip to content

Web Component

Use this approach when your target project is not a React app (Angular, Vue, plain HTML, or any other framework). Workflow Builder is wrapped in a standard custom element (<workflow-builder>) and consumed as an ES module or UMD bundle.

Replace the standard React root mount with a Web Component class:

import { StrictMode } from 'react';
import * as ReactDOM from 'react-dom/client';
import { App } from './app/app';
import './app/features/i18n';
class WorkflowBuilder extends HTMLElement {
private root: ReactDOM.Root | null = null;
connectedCallback() {
if (!this.root) {
const container = document.createElement('div');
this.appendChild(container);
this.root = ReactDOM.createRoot(container);
this.root.render(
<StrictMode>
<App />
</StrictMode>,
);
}
}
disconnectedCallback() {
if (this.root) {
this.root.unmount();
this.root = null;
}
}
}
customElements.define('workflow-builder', WorkflowBuilder);
<workflow-builder></workflow-builder>
// vite.config.mts
build: {
lib: {
entry: path.resolve(__dirname, './src/main.tsx'),
name: 'WorkflowBuilder',
fileName: (format) => `workflow-builder.${format}.js`,
formats: ['es', 'umd'],
},
outDir: 'dist/package',
},

After building, add a package.json inside dist/package/:

{
"name": "workflow-builder",
"version": "1.0.0",
"main": "workflow-builder.umd.js",
"module": "workflow-builder.es.js",
"style": "frontend.css",
"peerDependencies": {
"react": "^19.1.0",
"react-dom": "^19.1.0"
},
"files": ["workflow-builder.umd.js", "workflow-builder.es.js", "frontend.css"]
}

Drop the built files into your project and use the custom element anywhere:

<script src="workflow-builder.es.js" type="module"></script>
<link rel="stylesheet" href="frontend.css" />
<workflow-builder></workflow-builder>

Works in React, Angular, Vue, or plain HTML. Note that React and ReactDOM are listed as peer dependencies, so the host application must provide them.

The web component renders in the light DOM (no Shadow DOM). This means the host page’s CSS may affect Workflow Builder’s styles and vice versa. If you need full style isolation, wrap the component in a Shadow DOM root:

connectedCallback() {
if (!this.root) {
const shadow = this.attachShadow({ mode: 'open' });
const container = document.createElement('div');
shadow.appendChild(container);
this.root = ReactDOM.createRoot(container);
this.root.render(
<StrictMode>
<App />
</StrictMode>,
);
}
}

Note that when using Shadow DOM, you must inject Workflow Builder’s stylesheet into the shadow root rather than the document head.

ApproachProsCons
Web ComponentFramework-agnostic, standardized APIRequires wrapper setup
iFrameFull isolation, no conflictsLimited interaction, styling constraints
Module FederationRuntime dependency sharingRequires Webpack support in host
Framework adapters (react2angular, etc.)Deep integrationPer-framework maintenance burden