Loading...
Area: Episerver CMS
Applies to versions: 10 and higher

Creating a React component

Episerver has support for using such JavaScript frameworks in the user interface, which means that you can use them to build components used by editors in the on-page edit view.

In this section, we will go through how you can use the JavaScript framework React in your Episerver user interface, but most concept should be applicable to other JavaScript frameworks as well.

Components can be added to different parts of the user interface, this example describes how to create a custom edit view.

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:

[ServiceConfiguration(typeof(ViewConfiguration))]
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 view, you will have an extra view listed in the view switcher.

React_component_view_switcher.png

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 (see https://reactjs.org/docs/react-dom.html#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 this code:

index.js

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 () {
        ReactDOM.unmountComponentAtNode(this.domNode);
    }
});

This creates a widget which will then render the React component to its 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 user interface, it needs to be bundled into a single file (the one that our view configuration is pointing to). To do this, you can use bundlers like webpack or rollup. Examples of both methods are given below.

The first thing to note is that since 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 with 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 should not 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 include what you configured in the view configuration. Change the paths in the examples below to suit your needs.

webpack.config.js

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: [
        "dojo/_base/declare",
        "dijit/_WidgetBase"
    ]
};

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.

rollup.config.js

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: [
        babel({
            exclude: "node_modules/**"
        }),
        commonjs(),
        replace({ "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV) }),
        resolve()
    ],
    external: [
        "dojo/_base/declare",
        "dijit/_WidgetBase"
    ]
};

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 user interface. But what if you want 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.

In that case, you can use Ben McKernan's React component as inspiration. The code for this component is found on GitHub and 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. (Note that this component is not officially supported.)

Events are automatically be connected from the props to the widget based on the naming convention onEventName; and settings for the widget are only passed to the widget during construction. Changes to settings are not 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) {
        super(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 (
            <div>
                <DojoWidget type="epi-cms/widget/ContentSelector" settings={{ repositoryKey: "pages" }} onChange={this.handleChange} />
                {label}
            </div>
        );
    }
}

export default MyComponent;

Something else that maybe worth looking at is the @episerver/amd-proxy-loader mentioned earlier (see also https://www.npmjs.com/package/@episerver/amd-loader-proxy). 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.

Last updated: May 02, 2019

Feedback?