# Add a Configuration MFE in App Builder
Entando MFEs can be customized through an App Builder feature that uses a specialized micro frontend. This tutorial splits the process into 4 steps:
- Modify an existing MFE (the target MFE) to take a configuration option
- Create a new MFE (the config MFE) to provide a user interface for the configuration option
- Set up the target MFE to use the configuration provided by the config MFE
- Publish and test the configurable MFE
# Prerequisites
# Step 1: Add a Configuration Option to a Target MFE
Start by adding a configuration option to an existing MFE. If you don't already have one, you can create it via the React MFE tutorial.
# Add an Attribute to the Custom Element
- Replace the contents of
src/custom-elements/WidgetElement.jsxwith the following code to add attribute handling to the custom element and re-render the app when an attribute changes. This enables the Entando-providedconfigto be passed as a property to the React root component (App).
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from '../App';
const ATTRIBUTES = {
config: 'config'
};
class WidgetElement extends HTMLElement {
static get observedAttributes() {
return Object.values(ATTRIBUTES);
}
connectedCallback() {
this.mountPoint = document.createElement('div');
this.appendChild(this.mountPoint);
this.root = createRoot(this.mountPoint);
this.render();
}
attributeChangedCallback(attribute, oldValue, newValue) {
if (!WidgetElement.observedAttributes.includes(attribute)) {
throw new Error(`Untracked changed attributes: ${attribute}`)
}
if (this.mountPoint && newValue !== oldValue) {
this.render();
}
}
render() {
const attributeConfig = this.getAttribute(ATTRIBUTES.config);
const config = attributeConfig && JSON.parse(attributeConfig);
this.root.render(
<App config={config} />
);
}
}
customElements.define('simple-mfe', WidgetElement);
- Replace the contents of
src/App.jsxwith the following. This component now receives theconfigproperty and displays thenameparameter it contains. This turns the static component from the React MFE tutorial into a more dynamic component.
import './App.css';
function App({config}) {
const { params } = config || {};
const { name } = params || {};
return (
<div className="simple-mfe">
<p>
Hello, {name}!
</p>
</div>
);
}
export default App;
- For test purposes, add a configuration file
public/mfe-config.jsonwith the following content:
{
"params": {
"name": "Jane Smith"
}
}
- In
index.html, at the root ofsimple-mfe, replace the<body>with the following. This allows you to set the MFEconfigattribute and test locally with the same configuration structure provided by Entando. Keep the/src/main.jsxscript: Vite loads the app through it.
<body>
<simple-mfe></simple-mfe>
<script type="module" src="/src/main.jsx"></script>
<script>
function injectConfigIntoMfe() {
fetch('/mfe-config.json').then(async response => {
const config = await response.text()
const mfeEl = document.getElementsByTagName('simple-mfe')[0]
mfeEl.setAttribute('config', config)
})
}
injectConfigIntoMfe()
</script>
</body>
- Start the app and confirm that "Hello, Jane Smith!" is displayed. Use Ctrl+C to close the app.
ent bundle run simple-mfe
# Step 2: Create a Config MFE
Next, create a new MFE for managing the configuration option. These steps are very similar to the React MFE tutorial.
- Add a new microfrontend to your bundle project:
ent bundle mfe add simple-mfe-config --type=widget-config
- Generate a new React app with Vite, then prepare it as in the React MFE tutorial, using
simple-mfe-configwherever that tutorial sayssimple-mfe: install the dependencies, add thestartscript, set"buildFolder": "dist"onsimple-mfe-configinentando.json, and configure library mode as described in Build a Single File for Entando.
npm create vite@latest microfrontends/simple-mfe-config -- --template react
- Start the app:
ent bundle run simple-mfe-config
- Create a
microfrontends/simple-mfe-config/src/WidgetElement.jsxcomponent with the following content to set up the custom element for the config MFE.
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
class WidgetElement extends HTMLElement {
constructor() {
super();
this.params = {};
this.mountPoint = null;
this.root = null;
}
// Read by the App Builder when the user saves the form.
get config() {
return this.params;
}
// Written by the App Builder when the form opens on an already configured widget.
set config(value) {
this.params = value || {};
this.render();
}
connectedCallback() {
this.mountPoint = document.createElement('div');
this.appendChild(this.mountPoint);
this.root = createRoot(this.mountPoint);
this.render();
}
render() {
if (!this.root) {
return;
}
this.root.render(
<App
params={this.params}
onChange={params => {
this.params = params;
this.render();
}}
/>
);
}
}
customElements.define('simple-mfe-config', WidgetElement);
App Builder integration
- A config MFE must retain its state in a
configproperty - The App Builder supplies the
configproperty when the config MFE is rendered - When a user saves the form, the App Builder automatically persists the configuration through Entando APIs
Why the custom element owns the values
The App Builder reads config back from the element when the user saves, so config has to
return the current state of the form. createRoot().render() returns nothing, so there is
no component instance to read a state from — the custom element keeps the values and passes them
to React as props, and React reports edits back through onChange.
This is also why a config MFE cannot be wrapped with a generic React-to-web-component helper: those
expose config as a write-only prop, so on save the App Builder would read back the values the form
started with and every edit would be lost.
- Replace the contents of
src/App.jsxwith the following to add a simple form for managing a singlenamefield
import React from 'react';
function App({ params, onChange }) {
const handleChange = e => {
const input = e.target;
onChange({ ...params, [input.name]: input.value });
};
return (
<div>
<h1>Simple MFE Configuration</h1>
<div>
<label htmlFor="name">Name </label>
<input id="name" name="name" value={params.name || ''} type="text" onChange={handleChange} />
</div>
</div>
);
}
export default App;
TIP
- When the config MFE is displayed within the App Builder, the App Builder styles will be applied.
- Replace the contents of
src/main.jsxwith the following:
import './WidgetElement';
Don't import src/index.css here. A config MFE renders inside the App Builder, so the template's global rules for body, h1, p and so on would restyle the App Builder itself.
- For test purposes, add a configuration file
microfrontends/simple-mfe-config/public/mfe-config.jsonwith the following content. Note: the JSON for a config MFE contains just parameters so it is simpler than the JSON for a target MFE.
{
"name": "John Brown"
}
- In
index.html, at the root ofsimple-mfe-config, replace the<body>with the following. This allows you to set the config MFE parameters and test locally. Keep the/src/main.jsxscript: Vite loads the app through it.
<body>
<simple-mfe-config></simple-mfe-config>
<script type="module" src="/src/main.jsx"></script>
<script>
function injectConfigIntoMfe() {
fetch('/mfe-config.json').then(async response => {
const config = await response.json()
const mfeEl = document.getElementsByTagName('simple-mfe-config')[0]
mfeEl.config = config
})
}
injectConfigIntoMfe()
</script>
</body>
# Step 3: Configure the Target MFE to Use the Config MFE
- Edit the
entando.jsonand add these properties to thesimple-mfein order to connect the config MFE to this target MFE and specify the available params.
"configMfe": "simple-mfe-config",
"params": [
{
"name": "name",
"description": "The name for Hello World"
}
]
# Step 4: Publish and Test the Configurable MFE
Build and install the bundle with the following commands. For more details, see the Build and Publish tutorial.
ent bundle packent bundle publishent bundle deployent bundle installTest the full setup by adding the widget into an existing page. The config MFE should be displayed when the widget is first added to the page.
Fill out the
namefield and clickSave. You can update the widget configuration at any point by clickingSettingsfrom the widget actions in the Page Designer.Publish the page and confirm the target MFE is configured and displays correctly.