Product
Resources
Maxime Castres
November 24, 2020
Our community is looking for talented writers who are passionate about our ecosystem (jamstack, open-source, javascript) and willing to share their knowledge/experiences through our Write for the community program.
Many of you don't want to settle for the default WYSIWYG editor in Strapi. This is why you are looking to change the current editor and with any luck you will come across the excellent tutorial from Soupette which teaches you how to change the default editor to CKEditor.
It turns out that, according to our analytics and the number of requests on our slack, this tutorial is highly requested and it's an important resource for you or your team!
It was obvious to offer a second tutorial allowing you, this time, to use the Quill editor, more specifically react-quill. To do this we are going to create a new Field in the administration panel! Nothing very complicated and it won't take you more than 5 min.
Let's dive in!
Note: You should know that quill is an HTML editor and not a Markdown one. Be careful when changing editors on an existing app. If you have existing content in Markdown then you will need to convert it to HTML.
The first thing to do is to take an existing Strapi project. For my part I will simply create a new one.
yarn create strapi-app strapi-quill-editor --quickstart
Now I'm going to create my admin user and a very simple collection-type with just a Rich text field.
Now you can shut down your Strapi server in the terminal by pressing Ctrl+C.
As I said earlier, we are going to create a new Field in the administration panel in order to change our editor. You can find a documentation about this just right here but stick with me, we are going to follow this documentation.
Start by generating a new plugin:
yarn strapi generate:plugin wysiwyg
Now you need to dive in this plugin folder and to install react-quill:
cd plugins/wysiwyg
yarn add react-quill
Perfect! Let's launch our server in --watch-admin
for running the front-end development tool
# Go back to the Strapi root folder
cd ../..
yarn develop --watch-admin
In this part we will create three components:
QuillEditor which will be the implementation of the new WYSIWYG
Create a ./plugins/wysiwyg/admin/src/components/MediaLib/index.js
file containing the following:
import React, { useEffect, useState } from 'react';
import { useStrapi, prefixFileUrlWithBackendUrl } from 'strapi-helper-plugin';
import PropTypes from 'prop-types';
const MediaLib = ({ isOpen, onChange, onToggle }) => {
const {
strapi: {
componentApi: { getComponent },
},
} = useStrapi();
const [data, setData] = useState(null);
const [isDisplayed, setIsDisplayed] = useState(false);
useEffect(() => {
if (isOpen) {
setIsDisplayed(true);
}
}, [isOpen]);
const Component = getComponent('media-library').Component;
const handleInputChange = data => {
if (data) {
const { url } = data;
setData({ ...data, url: prefixFileUrlWithBackendUrl(url) });
}
};
const handleClosed = () => {
if (data) {
onChange(data);
}
setData(null);
setIsDisplayed(false);
};
if (Component && isDisplayed) {
return (
<Component
allowedTypes={['images', 'videos', 'files']}
isOpen={isOpen}
multiple={false}
noNavigation
onClosed={handleClosed}
onInputMediaChange={handleInputChange}
onToggle={onToggle}
/>
);
}
return null;
};
export default MediaLib;
We don't need to change this file! Leave it like this. Let's move on to the next component!
Create a ./plugins/wysiwyg/admin/src/components/Wysiwyg/index.js
file containing the following:
import React, { useState } from "react";
import PropTypes from "prop-types";
import { Button } from "@buffetjs/core";
import { Label, Description, ErrorMessage } from "@buffetjs/styles";
import Editor from "../QuillEditor";
import MediaLib from "../MediaLib";
const Wysiwyg = ({ inputDescription, error, label, name, onChange, value }) => {
const [isOpen, setIsOpen] = useState(false);
const handleChange = (data) => {
if (data.mime.includes("image")) {
const imgTag = `<p><img src="${data.url}" caption="${data.caption}" alt="${data.alternativeText}"></img></p>`;
const newValue = value ? `${value}${imgTag}` : imgTag;
onChange({ target: { name, value: newValue } });
}
// Handle videos and other type of files by adding some code
};
const handleToggle = () => setIsOpen((prev) => !prev);
const hasError = Boolean(error);
return (
<div
style={{
marginBottom: "1.6rem",
fontSize: "1.3rem",
fontFamily: "Lato",
}}
>
<div style={{ position: 'absolute', right: '15px', top: '-10px' }}>
<Button color="primary" onClick={handleToggle}>
MediaLib
</Button>
</div>
<Label htmlFor={name} style={{ marginBottom: 10 }} >{label} </Label>
<Editor name={name} onChange={onChange} value={value} />
{!hasError && inputDescription && (
<Description>{inputDescription}</Description>
)}
{hasError && <ErrorMessage>{error}</ErrorMessage>}
<MediaLib
onToggle={handleToggle}
isOpen={isOpen}
onChange={handleChange}
/>
</div>
);
};
export default Wysiwyg;
As you can see we are importing our MediaLib on top of our Editor imported from ../QuillEditor
. It doesn't exist yet so let's create it!
Create a ./plugins/wysiwyg/admin/src/components/QuillEditor/index.js
file containing the following:
import React, { useState } from "react";
import PropTypes from 'prop-types';
import ReactQuill from 'react-quill';
import 'react-quill/dist/quill.snow.css';
const Editor = ({ onChange, name, value }) => {
const modules = {
toolbar: [
[{ 'header': '1'}, {'header': '2'}, { 'font': [] }],
[{size: []}],
['bold', 'italic', 'underline','strike', 'blockquote'],
[{'list': 'ordered'}, {'list': 'bullet'}, {'indent': '-1'}, {'indent': '+1'}],
['link'],
['clean']
],
}
return (
<ReactQuill
theme="snow"
value={value}
modules={modules}
onChange={(content, event, editor) => {
onChange({ target: { name, value: content } });
}}/>
);
};
export default Editor;
Since the goal of this plugin is to override the current WYSIWYG we don't want it to be displayed in the administration panel but we need it to register our new Field. In order to do so, we will simply modify the front-end entry point of our plugin:
./plugins/wysiwyg/admin/src/index.js
with the following code:import pluginPkg from '../../package.json';
import pluginId from './pluginId';
import App from './containers/App';
import Initializer from './containers/Initializer';
import lifecycles from './lifecycles';
import trads from './translations';
import Wysiwyg from './components/Wysiwyg';
export default strapi => {
const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
const icon = pluginPkg.strapi.icon;
const name = pluginPkg.strapi.name;
const plugin = {
blockerComponent: null,
blockerComponentProps: {},
description: pluginDescription,
icon,
id: pluginId,
initializer: Initializer,
injectedComponents: [],
isReady: false,
isRequired: pluginPkg.strapi.required || false,
layout: null,
lifecycles,
mainComponent: App,
name,
preventComponentRendering: false,
trads,
menu: {
pluginsSectionLinks: [
{
destination: `/plugins/${pluginId}`,
icon,
label: {
id: `${pluginId}.plugin.name`,
defaultMessage: name,
},
name,
permissions: [
// Uncomment to set the permissions of the plugin here
// {
// action: '', // the action name should be plugins::plugin-name.actionType
// subject: null,
// },
],
},
],
},
};
strapi.registerField({ type: 'wysiwyg', Component: Wysiwyg });
return strapi.registerPlugin(plugin);
};
Here we are basically adding the WYSIWYG
component and registering it as a new Field.
If you already know Quill editor, you might see that I didn't import the image module. It is because we are using a button for the MediaLib. Instead of having to click the image button to import all your images, you can simply upload all your assets in the MediaLib and then add them directly to your content using the button on the right side!
Now you can stop you server by pressing Ctrl+c and build your admin by running the following command:
yarn build
yarn develop
However if you don't want to use the MediaLib, you simply need to:
./plugins/wysiwyg/admin/src/components/MediaLib
componentRemove the component from the Wrapper, here is the code of the wrapper without the MediaLib:
import React, { useState } from "react";
import PropTypes from "prop-types";
import { Button } from "@buffetjs/core";
import { Label, Description, ErrorMessage } from "@buffetjs/styles";
import Editor from "../QuillEditor";
const Wysiwyg = ({ inputDescription, error, label, name, onChange, value }) => {
const [isOpen, setIsOpen] = useState(false);
const handleChange = (data) => {
if (data.mime.includes("image")) {
const imgTag = `<p><img src="${data.url}" caption="${data.caption}" alt="${data.alternativeText}"></img></p>`;
const newValue = value ? `${value}${imgTag}` : imgTag;
onChange({ target: { name, value: newValue } });
}
// Handle videos and other type of files by adding some code
};
const handleToggle = () => setIsOpen((prev) => !prev);
const hasError = Boolean(error);
return (
<div
style={{
marginBottom: "1.6rem",
fontSize: "1.3rem",
fontFamily: "Lato",
}}
>
<Label htmlFor={name} style={{ marginBottom: 10 }} >{label} </Label>
<Editor name={name} onChange={onChange} value={value} />
{!hasError && inputDescription && (
<Description>{inputDescription}</Description>
)}
{hasError && <ErrorMessage>{error}</ErrorMessage>}
</div>
);
};
export default Wysiwyg;
Then you simply need to add the image module in the Quill editor by updating the ./plugins/wysiwyg/admin/src/components/QuillEditor/index.js
file:
...
const modules = {
toolbar: [
[{ 'header': '1'}, {'header': '2'}, { 'font': [] }],
[{size: []}],
['bold', 'italic', 'underline','strike', 'blockquote'],
[{'list': 'ordered'}, {'list': 'bullet'}, {'indent': '-1'}, {'indent': '+1'}],
['link', 'image'],
['clean']
],
}
...
yarn build
yarn develop
That's it! You should be able to use Quill editor now! Thanks for following this tutorial, see you next time ;)
Please note: Since we initially published this blog post, we released new versions of Strapi and tutorials may be outdated. Sorry for the inconvenience if it's the case. Please help us by reporting it here.
Get started with Strapi by creating a project, using a starter or trying our instant live demo. Also, join our academy to become a Strapi expert, and consult our forum if you have any questions. We will be there to help you.
See you soon!Sign up for the Strapi newsletter to keep up with the latest news from the Strapi community!