Overview
In this article, we will run FormEngine inside Electron, and also make
the Monaco editor work locally, without CDN.
Bootstrap application
We will use electron-react-boilerplate to create our simple
Electron application. Open your shell and execute the following commands:
git clone --depth 1 --branch main https://github.com/electron-react-boilerplate/electron-react-boilerplate.git electron-formengine
cd electron-formengine
npm install
Now you can run application by executing following command:
Let’s install the packages, run the following commands:
npm install @react-form-builder/designer @react-form-builder/components-rsuite
npm install
Now change the contents of the App.tsx file:
const builderView = new BuilderView([])
function App() {
return <FormBuilder view={builderView}/>
}
export default App
Great, now you can launch the application and see the error loading the Monaco scripts:
The thing is that the Monaco editor is set to download from CDN by default. Let’s change this behavior.
Monaco settings
First of all, we need to add settings to load the Monaco editor from the local NPM package. Create a file src/renderer/monaco-settings.ts
with the following contents:
src/renderer/monaco-settings.ts
// eslint-disable-next-line no-restricted-globals
self.MonacoEnvironment = {
getWorkerUrl: (_, label) => {
if (label === 'json') {
return './json.worker.bundle.js';
}
if (label === 'css' || label === 'scss' || label === 'less') {
return './css.worker.bundle.js';
}
if (label === 'html' || label === 'handlebars' || label === 'razor') {
return './html.worker.bundle.js';
}
if (label === 'typescript' || label === 'javascript') {
return './ts.worker.bundle.js';
}
return './editor.worker.bundle.js';
},
};
loader.config({monaco});
FormEngine is using JavaScript code actions, which violates the CSP policies defined in the src/renderer/index.ejs file. Remove the meta
tag for “Content-Security-Policy” and your file will look like this:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Hello Electron React!</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
Now let’s add the use of monaco-settings to App.tsx, and add the components. Change the contents of App.tsx to the following:
const components = rSuiteComponents.map((c) => c.build());
const builderView = new BuilderView(components);
export default function App() {
return (
<div style={{width: '97vw', height: '97vh'}}>
<FormBuilder view={builderView}/>
</div>
);
}
The last bit is fixing the webpack configuration files to package Monaco workers. You can find many examples of how to do this in
the official Monaco repository. Take a look at
this example.
Open .erb/configs/webpack.config.renderer.dev.ts and change entry to the highlighted one, also change output.filename as below:
.erb/configs/webpack.config.renderer.dev.ts
const configuration: webpack.Configuration = {
//...
entry: {
client: `webpack-dev-server/client?http://localhost:${port}/dist`,
devServer: 'webpack/hot/only-dev-server',
renderer: path.join(webpackPaths.srcRendererPath, 'index.tsx'),
'editor.worker': 'monaco-editor/esm/vs/editor/editor.worker.js',
'json.worker': 'monaco-editor/esm/vs/language/json/json.worker',
'css.worker': 'monaco-editor/esm/vs/language/css/css.worker',
'html.worker': 'monaco-editor/esm/vs/language/html/html.worker',
'ts.worker': 'monaco-editor/esm/vs/language/typescript/ts.worker',
},
output: {
path: webpackPaths.distRendererPath,
publicPath: '/',
filename: '[name].bundle.js',
library: {
type: 'umd',
},
},
//...
};
Now let’s add similar changes to the .erb/configs/webpack.config.renderer.prod.ts file - change entry and output.filename:
.erb/configs/webpack.config.renderer.prod.ts
const configuration: webpack.Configuration = {
//...
entry: {
renderer: path.join(webpackPaths.srcRendererPath, 'index.tsx'),
'editor.worker': 'monaco-editor/esm/vs/editor/editor.worker.js',
'json.worker': 'monaco-editor/esm/vs/language/json/json.worker',
'css.worker': 'monaco-editor/esm/vs/language/css/css.worker',
'html.worker': 'monaco-editor/esm/vs/language/html/html.worker',
'ts.worker': 'monaco-editor/esm/vs/language/typescript/ts.worker',
},
output: {
path: webpackPaths.distRendererPath,
publicPath: './',
filename: '[name].bundle.js',
library: {
type: 'umd',
},
},
//...
};
Running application
Launch the application using the command:
You will see something similar to what is shown in the screenshot below (the CSP warning only exists in development mode):
Go to the “Settings” tab in the left panel and click “Add a code action” button. The code editor has successfully loaded:
Building for production
To package the application for production run the command:
You can find the binaries for your platform in the release/build folder. For example, for macOS this would be release/build/mac-arm64.
Let’s check how the CSS editor works. Select the “Screen” component, and then open the “Style” tab in the right pane. Start typing text in
the code editor, notice that IntelliSense is working:
Conclusion
Packaging a FormEngine application in Electron is not much different from packaging any other React application, only packaging the Monaco
editor can cause difficulties. We hope this article will help you save your time.
Next steps
Last modified on April 16, 2026