Creating a custom widget for Netlify CMS
Tags: javascript, webdev, react, tutorial
Published: 2019-12-06 12:05:40

Hi, devs!

Today I would like to share a few things I've found recently working with Netlify CMS. This CMS has a set of built-in fields,  that you can use in your schema. Those fields are called "widgets" in the Netlify CMS ecosystem. Each widget describes the specific entry data type. For example, there are widgets `Date`, `Text`, `Boolean`, etc. You can find all available widgets in [the official docs](https://www.netlifycms.org/docs/widgets).

Most of the time, you probably will be fine with these built-in widgets. But it's also nice to look forward and think in advance, what if someday I need more functionality than the original widgets provide? Luckily, authors of Netlify CMS thought of this also and made it possible to create and register a custom widget. Moreover, you even can find a few articles that show and explain how to do that. But, all of them weren't clear enough for me, they are hiding quite important details of the implementation. So I had to read all of the articles to make it work. So the goal of this post is putting all of the important things together.

_If you are not interested in further reading and want to see the actual code, you can directly go to the [demo on codesandbox](https://codesandbox.io/s/nifty-black-s8eih) or [github repo](https://github.com/room-js/netlify-cms-custom-widget)_

## Let's write some code

A custom widget consists of two different React.js components: "control" and "preview". The first one is taking input from a user and the second one is responsible for how the user input data will be rendered in the preview area. As far as I understood, the preview component is optional and if you don't have it the data will be rendered in the preview anyway, but you have no control over the styling in this case.

So, let's start with writing these two React.js components.

I made the "control" component based on `CKEditor`, which is a rich text editor (similar to built-in `Markdown` widget).

```javascript
// Control component - src/components/customWidget/CustomWidgetControl.js
import React, { PureComponent } from 'react';
import CKEditor from '@ckeditor/ckeditor5-react';
import ClassicEditor from '@ckeditor/ckeditor5-build-classic';

export class CustomWidgetControl extends PureComponent {
  onChange = (event, editor) => {
    const data = editor.getData();
    this.props.onChange(data);
  }

  render() {
    return (
      <CKEditor
        editor={ ClassicEditor }
        onChange={this.onChange}
      />
    );
  }
}
```

#### The important things about the control component:
1. **It CAN NOT be a functional component (React.FC)**. If you make it FC it will not save data for some reason, so be aware of it.
2. You need to update the stored data manually. For this, the control component has a prop `onChange` that receives the new value as a parameter. We need to call this prop on every change.

```javascript
// Preview component - src/components/customWidget/CustomWidgetPreview.js
import React from 'react';

export const CustomWidgetPreview = (props) => {
  return (
    <div dangerouslySetInnerHTML={{ __html: props.value}} />
  );
}
```

What we need to know here is just that **we get the value from the component's props**. In this case, we get the HTML string and render it as a raw HTML.

### Register the widget

So, we have created components for our custom widget. Now we need to introduce this widget to Netlify CMS, make it visible for it. Talking by Netlify CMS' terminology, we have to register our widget.

```javascript
// src/cms/cms.js
import CMS from 'netlify-cms-app';
import { CustomWidgetControl, CustomWidgetPreview } from '../components/customWidget';

CMS.registerWidget('mywidget', CustomWidgetControl, CustomWidgetPreview);
```

`registerWidget()` method accepts three arguments: widget name, control component and preview component. The last one is optional as I mentioned above.

Make sure that the JavaScript file with this code is injected into the CMS pages. In this particular case, it's done by `gatsby-plugin-netlify-cms` plugin, but it very depends on how you use Netlify CMS.

### Add the field to your schema
```yaml
// static/admin/config.yml
...
    fields:
      - {label: "Title", name: "title", widget: "string"}
      - {label: "My Custom Widget", name: "mywidgetname", widget: "mywidget"}

```

### Recap

1. Control component can not be a function
2. Call `onChange` prop in control component to update the value
3. Use `value` prop in the preview component to access the input data

I hope, this article will help you and save your time on research. Please let me know in comments if something stays unclear to you.

# Source code / Demo

{% github room-js/netlify-cms-custom-widget %}

{% codesandbox nifty-black-s8eih %}