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.
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 or github repo
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).
// 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:
- 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.
- 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.
// 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.
// 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
// static/admin/config.yml
...
fields:
- {label: "Title", name: "title", widget: "string"}
- {label: "My Custom Widget", name: "mywidgetname", widget: "mywidget"}
Recap
- Control component can not be a function
- Call
onChange
prop in control component to update the value - 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
room-js / netlify-cms-custom-widget
Small demo to show how to create a custom widget for Netlify CMS
netlify-cms-custom-widget
Small demo to show how to create a custom widget for Netlify CMS
The custom widget components are located in ./src/components/customWidget
folder.
Live demo on CodeSandbox
Top comments (4)
Hi @room_js ,
the last block of this page, the demo from CodeSandbox, displays this error:
Unhandled Rejection (SecurityError): The operation is insecure
Actually it's working fine on my end. Can't reproduce this error, tried FF and Chrome, in both everything is fine. As I see after googling this error might be related to Firefox, but again, it works for me in the latest Firefox. Not sure why you experience it...
Hi,
it works fine now, with Firefox extensions disabled.
I don't know exactly which one was faulty, but I've no time to debug this now
Thanks a lot for having a look!
Oh, I see. Extensions bring troubles sometimes indeed :) Good to hear that you figured that out!