---
title: Create a React component
slug: cms-13/docs/create-a-react-component
docTags: 
createdAt: 2026-07-30T20:22:01.868Z
---

Optimizely Content Management System (CMS) supports using JavaScript frameworks in the user interface to build components used by editors in the on-page edit view.

Add components to different parts of the user interface. This example describes how to create a custom edit view.

## Register the view server-side

Register a `ViewConfiguration` class on the server side and point to the JavaScript file to load. The file is your bundled React view. The following code registers the view:

```csharp
[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;
  }
}
```

In edit view, an extra view (**Custom view**) is listed in the view switcher.

## Create the entry point

A typical React application has an entry point called `index.js` that calls `ReactDOM.render` (see [ReactDOM Render section](https://reactjs.org/docs/react-dom.html#render)). Because the view runs inside a Dojo application, the entry point must act as a widget that calls `ReactDOM.render`. The following code creates this entry point:

`index.js`

```javascript
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 that renders the React component to its DOM node. It also unmounts the component when the view is destroyed.

Use this code as a boilerplate and replace `MyComponent` with your React component.

## Build the bundle

Bundle your `MyComponent` into a single file (the one your view configuration points to) to display it in the user interface. Use bundlers like [webpack](https://webpack.js.org/) or [rollup](https://rollupjs.org/). The following examples demonstrate both methods.

Because CMS uses AMD for module loading, the bundle must also be AMD formatted. The build tools handle this with a single configuration line.

In the entry point code from the previous example, there are imports for Dojo and Dijit. These exist inside the CMS application and should not be bundled, so mark them as external. This requires a few lines of configuration.

The entry point for your bundle is the `index.js` file from the previous example. The output file should be the path to the folder for your JavaScript file configured in your [module.config](docId\:dI64jrVOXLd4rWo8gsedO), plus the relative path to the file configured in the view configuration. Change the paths in the following examples to suit your needs.

`webpack.config.js`

```javascript
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 key parts of this configuration are the settings for `libraryTarget`, `libraryExport`, and `externals`.

For webpack, set `libraryExport` to return the default export from the entry point rather than an object containing the exports.

`rollup.config.js`

```javascript
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 key parts of this configuration are the settings for `format` and `external`.

## Access CMS features from the React component

After loading your React component in the user interface, use CMS components from within it. For example, let the user select a page using the content selector.

Events are automatically connected from the props to the widget based on the naming convention `onEventName`. Settings for the widget are only passed during construction. Setting changes are not propagated after mount.

The following example shows usage with a content selector:

```javascript
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;
```

The `@episerver/amd-loader-proxy` mentioned earlier (see also [@episerver/amd-loader-proxy](https://www.npmjs.com/package/@episerver/amd-loader-proxy)) dynamically requires AMD modules from CMS in your JavaScript application. This is framework agnostic, so use it with any JavaScript framework.
