Vulnerability in EPiServer.Forms

Try our conversational search powered by Generative AI!

Ben  McKernan
Nov 8, 2018
(5 votes)

A react widget in Episerver CMS (Revisited)

Back in January 2017, Magnus Baneryd created a blog post about creating a gadget using react ( Given this was nearly two years ago and the technology has changed a bit since then, I thought that I would revisit his idea.

I am going to assume that if this topic is interesting for you then you already have some knowledge about creating react applications and using different bundling tools. So I'm not going to explain what node modules you need need to install but rather just point out what might be different from a regular react application setup.

Aside: I his blog, Magnus creates a custom gadget. I personally hate the gadgets in the left and right panes since they are so small and adding more than two basically makes them unusable. So for this demo I will use a custom edit view since it is easy to demo. However this same approach will still work for the custom gadget.

Register the view server-side

To create a new edit view for content data we need to register a ViewConfiguration class on the server side and point to the JavaScript file that should be loaded. In this case the file will be our bundled react view. The following code will do this:

public class MyView : ViewConfiguration<IContentData>
    public MyView()
        Key = "my-view";
        Name = "React View";
        ControllerType = "alloy/components/ReactGadget";
        IconClass = "epi-iconStar";
        SortOrder = 100;

Now when you look in edit mode you will have an extra view listed in the view switcher.

Create the entry point

If you have ever made a react application before you will be familiar with having an entry point called index.js that simply calls ReactDOM.render ( However in this case, since our view in running inside a dojo application, the entry point needs to pretend to be a widget and that widget will instead call ReactDOM.render. This can be done with code that is basically the same as what Magnus had written in his blog post:


import React from "react";
import ReactDOM from "react-dom";
import declare from "dojo/_base/declare";
import WidgetBase from "dijit/_WidgetBase";
import MyComponent from "./MyComponent";

export default declare([WidgetBase], {
    postCreate: function () {
        ReactDOM.render(<MyComponent />, this.domNode);
    destroy: function () {

This will create a widget which will then render the react component to it's DOM node. It will also unmount the component when the view is destroyed.

This code can be used as a boilerplate, just replace MyComponent with your react component.

Building the bundle

At this point let's assume you have your MyComponent implemented in react. In order to get it to appear in the UI it needs to be bundled into a single file (the one that our view configuration is pointing to). Nowadays the defacto bundler seems to be webpack ( but I would argue that rollup ( is better suited in this scenario. However I will give an example of both configurations since they are very similar.

The main point to note is that since the Episerver CMS uses AMD for its module loading, the bundle we produce also needs to be AMD formatted. Luckily the build tools will do this for you will a single line of configuration.

The second thing to note is that in the entry point code, in the previous example, there are imports for dojo and dijit. These however exist inside the Episerver CMS application and shouldn't be bundled, so they need to be marked as external. This is also done with a few lines of configuration.

The entry point for your bundle will be the index.js file from the previous example and the output file should what you configured in the view configuration. Change the paths in the examples below to suit your needs.


const path = require("path");

module.exports = {
    entry: path.resolve(__dirname, "src/index.js"),
    output: {
        filename: "ReactGadget.js",
        libraryTarget: "amd",
        libraryExport: "default",
        path: path.resolve(__dirname, "ClientResources/Scripts/components/")
    module: {
        rules: [
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: "babel-loader"
    externals: [

The interesting parts of this configuration are the settings for libraryTarget, libraryExport, and externals.

For webpack it is important to set the libraryExport so that it returns the default export from the entry point rather than an object containing all the exports.


import babel from "rollup-plugin-babel";
import commonjs from "rollup-plugin-commonjs";
import replace from "rollup-plugin-replace";
import resolve from "rollup-plugin-node-resolve";

export default {
    input: "src/index.js",
    output: {
        file: "ClientResources/Scripts/components/ReactGadget.js",
        format: "amd"
    plugins: [
            exclude: "node_modules/**"
        replace({ "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV) }),
    external: [

The interesting parts of this configuration are the settings for format, and external.

Accessing CMS features from the react component

At this point you should have your react component loading in the UI. But as you develop your great new react view you may realise that it would be really nice to be able to use a component from the Episerver CMS. For example, you may want the user to be able to select a page using the content selector.

Well luckily I have created a react component that will do exactly this for you. The code for this component can be copied from this gist into your project ( It has a dependency on @episerver/amd-loader-proxy so make sure you install this node module. I will note however that I have only done very limited testing on this component so there maybe some quirks.

Events will automatically be connected from the props to the widget based on the naming convention onEventName. And settings for the widget will only be passed to the widget during construction. Changes to settings will not be propagated after mount, although it is probably not hard to implement.

Example usage could look like this:

import React, { Component } from "react";
import DojoWidget from "./DojoWidget";

class MyComponent extends Component {

    constructor(props) {
        this.state = {
            value: null
        this.handleChange = this.handleChange.bind(this);

    handleChange(value) {
        this.setState({ value });

    render() {
        const { value } = this.state;
        const label = value ? <div>ContentLink: {value}</div> : null;

        return (
                <DojoWidget type="epi-cms/widget/ContentSelector" settings={{ repositoryKey: "pages" }} onChange={this.handleChange} />

export default MyComponent;

Something else that maybe worth looking at is the @episerver/amd-proxy-loader I mentioned previously ( With this it is possible to dynamically require AMD modules from Episerver CMS from your JavaScript application. This is framework agnostic so it can be used with whichever JavaScript framework you prefer.

Final words

This is quite a rough example but I hope it inspires someone to make something much better.

Nov 08, 2018


Please login to comment.
Latest blogs
Optimizely DXP - Leader in Forrester Wave Q4 2023

There are many reasons why its nominated as leader including below but not limited to: Developers friendly - devs and architect love to implement i...

Yagnik Jadav | Dec 11, 2023 | Syndicated blog

Live on Optimizely CMS 12 and .NET 8

"Better late than never" is a fitting saying here. We've finally gotten around to updating the website to Optimizely CMS 12 and .NET 8!

Allan Thraen | Dec 10, 2023 | Syndicated blog

Date property editor

The Optimizely CMS has built-in DateTime property. When editing, the Editor selects both the date and the time. Sometimes we would like to configur...

Grzegorz Wiecheć | Dec 9, 2023 | Syndicated blog

Update related content

In this article, I will show simple code that allow to replace linked content with other content selected by the Editor. When deleting content whos...

Grzegorz Wiecheć | Dec 8, 2023 | Syndicated blog

Getting Started with Optimizely SaaS Core and Next.js Integration: Content Areas and Blocks

The blog guide elaborates on improving content rendering by exploring content areas and blocks within the Optimizely CMS. It walks through setting ...

Francisco Quintanilla | Dec 8, 2023 | Syndicated blog

Maximize performance by uploading your external data to Optimizely Graph

Learn to integrate external data into Optimizely Graph for improved performance, covering data preparation, synchronization, and effective querying.

Surjit Bharath | Dec 6, 2023 | Syndicated blog