---
title: Create a React component
slug: cms-11/dev-docs/creating-a-react-component
docTags: 
createdAt: 2026-07-22T19:45:18.655Z
---

Optimizely Content Management System (CMS) supports JavaScript frameworks in the user interface. You can use them to build components editors use in the on-page edit view.

You can add components to different parts of the user interface. The following example describes how to create a custom edit view.

## Register the view server-side

To create an edit view for content data, register a `ViewConfiguration` class on the server side and point to the JavaScript file you want to load. In this case, the file is your bundled React view. The following code will do this:

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

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

![](https://api.archbee.com/api/optimize/BrbvVORKJ5iIeSMZye5kI/44QzTV0bkZg2fYvZYtI7B-20260722-194639.png "react_component_view_switcher.png")

## Create the entry point

If you have ever made a React application before, you are familiar with having an entry point called `index.js` that calls `ReactDOM.render`. However, in this case, because your view is running inside a Dojo application, the entry point needs to pretend to be a widget, and that widget will instead call `ReactDOM.render` using the following code:

**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 code creates a widget that renders the React component to its DOM node. It also unmounts the component when it destroys the view.

You can use this as a boilerplate by replacing `MyComponent` with your React component.

## Build the bundle

At this point, let's assume you have your `MyComponent` implemented in React. Bundle it into a single file (the one your view configuration points to) to display in the user interface. To do this, you can use bundlers like [webpack](https://webpack.js.org/) or [rollup](https://rollupjs.org/).

The first thing to note is that because CMS uses AMD for its module loading, the bundle you produce also needs to be AMD formatted. Luckily, the build tools will do this with a single configuration line.

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 CMS application and should not be bundled, so mark them as external with a few lines of configuration.

The entry point for your bundle is the `index.js` file from the previous example, and the output file should be the path to the folder for the JavaScript file you configured in your [module.config](docId\:dI64jrVOXLd4rWo8gsedO), plus the relative path to the file you configured in the view configuration. Change the paths in the examples below 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 interesting parts of this configuration are the settings for `libraryTarget`, `libraryExport`, and `externals`.

For webpack, set the `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 interesting parts of this configuration are the settings for `format` and `external`.

## Access 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 CMS? For example, you may want the user to be able to select a page using the content selector.

Events are automatically connected from the props to the widget based on the naming convention `onEventName`; and widget settings are only passed to the widget during construction. Setting changes are not propagated after mount, although this is probably not hard to implement.

An example usage could look like this:

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

Something else that may be worth looking at is the `@episerver/amd-loader-proxy` mentioned earlier. With this, you can dynamically require AMD modules from CMS from your JavaScript application. You can use this with whichever JavaScript framework you prefer.
