diff --git a/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Extend_CMS_Interface.md b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Extend_CMS_Interface.md
index 84fbad81e..d84c287eb 100644
--- a/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Extend_CMS_Interface.md
+++ b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Extend_CMS_Interface.md
@@ -204,26 +204,340 @@ To make the actions more user-friendly you can also use alternating buttons as
detailed in the [CMS Alternating Button](cms_alternating_button)
how-to.
-## ReactJS in SilverStripe
+## React components
-### SilverStripeComponent
+Some admin modules render their UI with React, a popular Javascript library created by Facebook.
+For these sections, rendering happens via client side scripts that create and inject HTML
+declaratively using data structures. These UI elements are known as "components" and
+represent the fundamental building block of a React-rendered interface.
-The base class for SilverStripe React components. If you're building React components for the CMS, this is the class you want to extend. `SilverStripeComponent` extends `React.Component` and adds some handy CMS specific behaviour.
-
-### Creating a component
-
-__my-component.js__
-```javascript
-import SilverStripeComponent from 'silverstripe-component';
-
-class MyComponent extends SilverStripeComponent {
-
-}
-
-export default MyComponent;
+For example, a component expressed like this:
+```js
+
+
+
+```
+
+Might actually render HTML that looks like this:
+```html
+
+
+
+
+
+
+```
+
+This syntax is known as JSX. It is transpiled at build time into native Javascript calls
+to the React API. While optional, it is recommended to express components this way.
+
+This documentation will stop short of explaining React in-depth, as there is much better
+documentation available all over the web. We recommend:
+* [The Official React Tutorial](https://facebook.github.io/react/tutorial/tutorial.html)
+* [Build With React](http://buildwithreact.com/tutorial)
+
+#### A few words about ES6
+The remainder of this tutorial is written in [ECMAScript 6](http://es6-features.org/#Constants), or _ES6_
+for short. This is the new spec for Javascript (currently ES5) that is as of this writing
+only partially implmented in modern browsers. Because it doesn't yet enjoy vast native support,
+it has to be [transpiled](https://www.stevefenton.co.uk/2012/11/compiling-vs-transpiling/) in order to work
+in a browser. This transpiling can be done using a variety of toolchains, but the basic
+ principle is that a browser-ready, ES5 version of your code is generated in your dev
+ environment as part of your workflow. Often called a "bundle," you should rarely have
+ to see this file. It is effectively an invisible layer that translates modern ES6
+ code into something a browser can parse. As browsers evolve, this step will become less
+ necessary. (Although, it is worth noting that because transpiling comes at such a low cost,
+ and browsers are relatively slow to catch up, we'll probably be using it for the
+ foreseeable future in order to adopt new features beyond ES6).
+
+ As stated above, there are many ways to solve the problem of transpiling. The toolchain
+ we use in core SilverStripe modules includes:
+ * [Babel](http://babeljs.io) (ES6 transpiler)
+ * [Webpack](http://webpack.js.org) (Module bundler)
+
+### Customising React components
+
+React components can be customised in a similar way to PHP classes, using a dependency
+injection API. The key difference is that components are not overriden the way backend
+services are. Rather, new components are composed using [higher order components](https://facebook.github.io/react/docs/higher-order-components.html).
+This has the inherent advantage of allowing all thidparty code to have an influence
+over the behaviour, state, and UI of a component.
+
+#### A simple higher order component
+
+Using our example above, let's create a customised `PhotoItem` that allows a badge,
+perhaps indicating that it is new to the gallery.
+
+```js
+const enhancePhoto = (PhotoItem) => (props) {
+ const badge = props.isNew ?
+ New!
:
+ null;
+
+ return (
+
+ );
+}
+
+const EnhancedPhotoItem = enhancedPhoto(PhotoItem);
+
+
+```
+
+Alternatively, this component could be expressed with an ES6 class, rather than a simple
+function.
+
+```js
+const enhancePhoto = (PhotoItem) => {
+ return class EnhancedPhotoItem extends React.Component {
+ render() {
+ const badge = this.props.isNew ?
+ New!
:
+ null;
+
+ return (
+
+ );
+
+ }
+ }
+}
+```
+
+When components are stateless, using a simple function in lieu of a class is recommended.
+
+#### Using the injector to customise a core component
+
+Let's make a more awesome text field. Because the `TextField` component is fetched
+through the injector, we can override it and augment it with our own functionality.
+
+In this example, we'll add a simple character count below the text field.
+
+First, let's create our higher order component.
+__my-module/js/components/CharacterCounter.js__
+```js
+import React from 'react';
+
+const CharacterCounter = (TextField) => (props) => {
+ return (
+
+
+ Character count: {props.value.length}
+
+ );
+}
+
+export default CharacterCounter;
+```
+
+Now let's add this higher order component to the injector.
+
+__my-module/js/main.js__
+```js
+import Injector from 'lib/Injector';
+import CharacterCounter from './components/CharacterCounter';
+
+Injector.update(
+ {
+ name: 'my-module',
+ },
+ wrap => {
+ wrap('TextField', CharacterCounter);
+ }
+);
+```
+
+Much like the configuration layer, we need to specify a name for this mutation. This
+will help other modules negotiate their priority over the injector in relation to yours.
+
+The second parameter of the `update` argument is a callback which receives a `wrap()` function
+that allows you to mutate the DI container with a wrapper for the component. Remember, this function does not _replace_
+the component -- it enhances it with new functionality.
+
+The last thing we'll have to do is make sure this script gets loaded into the admin
+page.
+
+__my-module/\_config/config.yml__
+
+```yaml
+ ---
+ Name: my-module
+ ---
+ SilverStripe\Admin\LeftAndMain:
+ extra_requirements_javascript:
+ # The name of this file will depend on how you've configured your build process
+ - 'my-module/js/dist/main.bundle.js'
+```
+Now that the customisation is applied, our text fields look like this:
+
+![](../../../_images/react-di-1.png)
+
+Let's add another customisation to TextField. If the text goes beyond a specified
+length, let's throw a warning in the UI.
+
+__my-module/js/components/TextLengthChecker.js__
+```js
+const TextLengthCheker = (TextField) => (props) => {
+ const {limit, value } = props;
+ const invalid = limit !== undefined && value.length > limit;
+
+ return (
+
+
+ {invalid &&
+
+ {`Text is too long! Must be ${limit} characters`}
+
+ }
+
+ );
+}
+
+export default TextLengthChecker;
+```
+
+We'll apply this one to the injector as well, but let's do it under a different name.
+For the purposes of demonstration, let's imagine this customisation comes from another
+module.
+
+__my-module/js/main.js__
+```js
+import Injector from 'lib/Injector';
+import TextLengthChecker from './components/TextLengthChecker';
+
+Injector.update(
+ {
+ name: 'my-other-module',
+ },
+ wrap => {
+ wrap('TextField', TextLengthChecker);
+ }
+);
+```
+
+Now, both components have applied themselves to the textfield.
+
+![](../../../_images/react-di-2.png)
+
+
+##### Getting multiple customisations to work together
+
+Both these enhancements are nice, but what would be even better is if they could
+work together collaboratively so that the character count only appeared when the user
+input got within a certain range of the limit. In order to do that, we'll need to be
+sure that the `TextLengthChecker` customisation is loaded ahead of the `CharacterCounter`
+customisation.
+
+First let's update the character counter to show characters _remaining_, which is
+much more useful. We'll also update the API to allow a `warningBuffer` prop. This is
+the amount of characters the input can be within the `limit` before the warning shows.
+
+__my-module/js/components/CharacterCounter.js__
+```js
+import React from 'react';
+
+const CharacterCounter = (TextField) => (props) => {
+ const { warningBuffer, limit, value: { length } } = props;
+ const remainingChars = limit - length;
+ const showWarning = length + warningBuffer >= limit;
+ return (
+
+
+ {showWarning &&
+ Characters remaining: {remainingChars}
+ }
+
+ );
+}
+
+export default CharacterCounter;
+```
+
+Now, when we apply this customisation, we need to be sure it loads _after_ the length
+checker in the middleware chain, as it relies on the prop `limit`. We can do that by specifying priority using `before` and `after`
+metadata to the customisation.
+
+__my-module/js/main.js__
+```js
+import Injector from 'lib/Injector';
+import CharacterCounter from './components/CharacterCounter';
+import TextLengthChecker from './components/TextLengthChecker';
+Injector.update(
+ {
+ name: 'my-module',
+ after: 'my-other-module',
+ },
+ wrap => {
+ wrap('TextField', CharacterCounter);
+ }
+);
+Injector.update(
+ {
+ name: 'my-other-module',
+ before: 'my-module',
+ },
+ wrap => {
+ wrap('TextField', TextLengthChecker);
+ }
+);
+```
+
+Now, both components play together nicely.
+
+![](../../../_images/react-di-3.png)
+
+### Registering new React components
+
+If you've created a module using React, it's a good idea to afford other developers an
+API to enhance those components. To do that, simply register them with `Injector`.
+
+__my-public-module/js/main.js__
+```js
+import Injector from 'lib/Injector';
+
+Injector.register('MyComponent', MyComponent);
+```
+
+Now other developers can customise your components with `Injector.update()`.
+
+Note: Overwriting components by calling `register()` multiple times for the same
+service name is discouraged, and will throw an error. Should you really need to do this,
+you can pass `{ force: true }` as the third argument to the `register()` function.
+
+### Using the injector within your component
+
+If your component has dependencies, you can add the injector via context using the `withInjector`
+higher order component.
+
+__my-module/js/components/Gallery.js__
+```js
+import React from 'react';
+import { withInjector } from 'lib/Injector';
+
+class Gallery extends React.Component {
+ render() {
+ const GalleryItem = this.context.injector.get('GalleryItem');
+ return (
+
+ {this.props.items.map(item => (
+
+ ))}
+
+ );
+ }
+}
+
+export default withInjector(Gallery);
```
-That's how you create a SilverStripe React component!
### Interfacing with legacy CMS JavaScript
diff --git a/docs/en/_images/react-di-1.png b/docs/en/_images/react-di-1.png
new file mode 100644
index 000000000..6b49649d9
Binary files /dev/null and b/docs/en/_images/react-di-1.png differ
diff --git a/docs/en/_images/react-di-2.png b/docs/en/_images/react-di-2.png
new file mode 100644
index 000000000..1f0075605
Binary files /dev/null and b/docs/en/_images/react-di-2.png differ
diff --git a/docs/en/_images/react-di-3.png b/docs/en/_images/react-di-3.png
new file mode 100644
index 000000000..ee975b0bf
Binary files /dev/null and b/docs/en/_images/react-di-3.png differ