44
loading...
This website collects cookies to deliver better user experience
<p>Text</p>
, <img src="..." />
and even <table>...</table>
. Therefore you should be aware of the potential security issues and how to overcome them.For TinyMCE to work, you will need to obtain an API Key by creating an account at Tinymce (the core editor is free 😍 )
If you are new to Strapi, make sure to take a look at this Quick Start Guide.
my-app
you can call it whatever you like.--quickstart
option will tell Strapi to create a basic project with default configurations and without templates, this is just to make the process easier and to avoid any complications.yarn create strapi-app my-app --quickstart
#or
npx create-strapi-app my-app --quickstart
Ctrl+C
or Command+C
and cd
into the project directory.cd my-app
wysiwyg
so we should run:yarn strapi generate:plugin wysiwyg
#or
npm run strapi generate:plugin wysiwyg
#or
strapi generate:plugin wysiwyg
@tinymce/tinymce-react
.cd
into the newly created plugin and only then install it there:cd plugins/wysiwyg
yarn add @tinymce/tinymce-react
#or
npm install @tinymce/tinymce-react
.../<your app name>/plugins/wysiwyg
):cd admin/src/
#The following will create .../MediaLib/index.js
mkdir -p components/MediaLib/; touch components/MediaLib/index.js
#The following will create .../Wysiwyg/index.js
mkdir -p components/Wysiwyg/; touch components/Wysiwyg/index.js
#The following will create .../Tinymce/index.js
mkdir -p components/Tinymce/; touch components/Tinymce/index.js
nano
), open the file:nano ./components/MediaLib/index.js
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);
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);
};
useEffect(() => {
if (isOpen) {
setIsDisplayed(true);
}
}, [isOpen]);
if (Component && isDisplayed) {
return (
<Component
allowedTypes={["images", "videos", "files"]}
isOpen={isOpen}
multiple={false}
noNavigation
onClosed={handleClosed}
onInputMediaChange={handleInputChange}
onToggle={onToggle}
/>
);
}
return null;
};
MediaLib.defaultProps = {
isOpen: false,
onChange: () => {},
onToggle: () => {},
};
MediaLib.propTypes = {
isOpen: PropTypes.bool,
onChange: PropTypes.func,
onToggle: PropTypes.func,
};
export default MediaLib;
nano ./components/Wysiwyg/index.js
file not found
error around the import TinyEditor...
Ignore it for now as we will create it in the next step.import React, { useState } from "react";
import PropTypes from "prop-types";
import { isEmpty } from "lodash";
import { Button } from "@buffetjs/core";
import { Label, InputDescription, InputErrors } from "strapi-helper-plugin";
import MediaLib from "../MediaLib";
import TinyEditor from "../Tinymce";
const Wysiwyg = ({
inputDescription,
errors,
label,
name,
noErrorsDescription,
onChange,
value,
}) => {
const [isOpen, setIsOpen] = useState(false);
let spacer = !isEmpty(inputDescription) ? (
<div style={{ height: ".4rem" }} />
) : (
<div />
);
if (!noErrorsDescription && !isEmpty(errors)) {
spacer = <div />;
}
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);
return (
<div
style={{
marginBottom: "1.6rem",
fontSize: "1.3rem",
fontFamily: "Lato",
}}
>
<Label htmlFor={name} message={label} style={{ marginBottom: 10 }} />
<div style={{ position: "absolute", right: "15px", top: "-10px" }}>
<Button color="primary" onClick={handleToggle}>
MediaLib
</Button>
</div>
<TinyEditor name={name} onChange={onChange} value={value} />
<InputDescription
message={inputDescription}
style={!isEmpty(inputDescription) ? { marginTop: "1.4rem" } : {}}
/>
<InputErrors
errors={(!noErrorsDescription && errors) || []}
name={name}
/>
{spacer}
<MediaLib
onToggle={handleToggle}
isOpen={isOpen}
onChange={handleChange}
/>
</div>
);
};
Wysiwyg.defaultProps = {
errors: [],
inputDescription: null,
label: "",
noErrorsDescription: false,
value: "",
};
Wysiwyg.propTypes = {
errors: PropTypes.array,
inputDescription: PropTypes.oneOfType([
PropTypes.string,
PropTypes.func,
PropTypes.shape({
id: PropTypes.string,
params: PropTypes.object,
}),
]),
label: PropTypes.oneOfType([
PropTypes.string,
PropTypes.func,
PropTypes.shape({
id: PropTypes.string,
params: PropTypes.object,
}),
]),
name: PropTypes.string.isRequired,
noErrorsDescription: PropTypes.bool,
onChange: PropTypes.func.isRequired,
value: PropTypes.string,
};
export default Wysiwyg;
nano ./components/Tinymce/index.js
API_KEY
with the actual key that you obtained from Tinymce.import React from "react";
import PropTypes from "prop-types";
import { Editor } from "@tinymce/tinymce-react";
const TinyEditor = ({ onChange, name, value }) => {
return (
<Editor
apiKey="API KEY"
value={value}
tagName={name}
onEditorChange={(editorContent) => {
onChange({ target: { name, value: editorContent } });
}}
outputFormat="text"
init={{}}
/>
);
};
TinyEditor.propTypes = {
onChange: PropTypes.func.isRequired,
name: PropTypes.string.isRequired,
value: PropTypes.string,
};
export default TinyEditor;
.../<your app name>/plugins/wysiwyg
nano index.js
import pluginPkg from "../../package.json";
import pluginId from "./pluginId";
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,
injectedComponents: [],
isReady: true,
isRequired: pluginPkg.strapi.required || false,
mainComponent: null,
name,
preventComponentRendering: false,
trads: {},
};
strapi.registerField({ type: "wysiwyg", Component: Wysiwyg });
return strapi.registerPlugin(plugin);
};
cd ../../../../
# After running this command I will be at .../my-app
# Make sure you are in .../<your-project-name>
yarn build --clean
#or
npm run build --clean
#or
strapi build --clean
--watch-admin
:
yarn develop --watch-admin
#or
npm run develop -- --watch-admin
#or
strapi develop --watch-admin
Content-Types Builder
so we can create new content for testing.Create new single type
Tinymce Test
.Test
and hit Finish
.Save
, and wait for the server to restartTinymce Test
, press it to edit it. And hop!, there you go, Tinymce is working! Yaaay 😍.--
watch-admin
mode, we don’t need to stop it, and we will still be able to see the changes we will make do as we are doing them (Cool ha? 😎).three
important things.nano plugins/wysiwyg/admin/src/components/Tinymce/index.js
outputFormat='text'
To: outputFormat='html'
init={{}}
add: selector: 'textarea',
<textarea></textarea>
tags for input.init={{}}
and after the previously added selector
, add two things:plugins: '',
Here we will add all the features and functionalities that we want Tinymce to have.toolbar: '',
It's also for adding features, but those who are added here will appear directly in the top toolbar of Tinymce, while the ones we added earlier will appear in a drop-down menu.' HERE '
and separate them with single spaces, A full list can be found here, Remember not to add any plugin that allows users to upload the media directly to the editor.#PATH: <your-project-name>/plugins/wysiwyg/admin/src/components/Tinymce/index.js
import React from "react";
import PropTypes from "prop-types";
import { Editor } from "@tinymce/tinymce-react";
const TinyEditor = ({ onChange, name, value }) => {
return (
<Editor
apiKey="API KEY"
value={value}
tagName={name}
onEditorChange={(editorContent) => {
onChange({ target: { name, value: editorContent } });
}}
outputFormat='html'
init={{
selector: 'textarea',
plugins: 'fullscreen insertdatetime .... MORE PLUGINS',
toolbar: 'code numlist bullist .... MORE PLUGINS',
}}
/>
);
};
TinyEditor.propTypes = {
onChange: PropTypes.func.isRequired,
name: PropTypes.string.isRequired,
value: PropTypes.string,
};
export default TinyEditor;
Ctrl+C
or Command+C
to stop Strapi.--watch-admin
, but after we build it clean:yarn build --clean
yarn develop
#OR
npm run build --clean
npm run develop
#OR
strapi build --clean
strapi develop
Tinymce Test
and give it another try, everything should be working fine 😆.