For TinyMCE 5: See how to add TinyMCE 5 to a simple React project.
After the roaring success of our Angular 2 and TinyMCE article, I am back to show you how to create a similar functioning piece of code. This time, using the React library. If you have already read the Angular 2 version, most of this will be familiar territory. Even so, I hope you will still have fun following along.
Prerequisites
This will not be a particularly advanced blog post, technically speaking, but I do expect you to have a working understanding of:
- JavaScript (including some of the newer, es2015 features)
- React
- How to use the terminal/command line
I also assume that you have Node and npm installed on your computer. We will write our code with the newest ES2015 syntax, which will be transpiled down to more browser friendly JavaScript by Babel in our build step. If all of this sounds scary and complicated, it really isn’t.
Setting up
Setting up a new project can be a very daunting process. Just as in the Angular 2 post, we will be using a marvelous tool that takes care of most setup for us. Meet create-react-app.
As you will soon see, using create-react-app makes the first setup process extremely easy and quick, but using create-react-app is optional to follow along in this tutorial. If you already have a project setup that you want to use you should be able to follow along with this guide – although you might have to solve some ‘gotchas’ and problems on your own that create-react-app takes care of otherwise.
Anyway, to start off we will install the create-react-app tool with the following command:
npm install create-react-app --global
After some installing, you now have the create-react-app command available in your terminal, so let’s start using it!
Creating the project
Using create-react-app, we will create a new project using the following command:
create-react-app tiny-react
Where tiny-react
is simply the name of the folder the project will be generated into, you could also just write create-react-app .
(Note: That’s a single period at the end to generate the project in the current directory.)
After the project has been generated and all the dependencies has been installed, just cd
into the project directory:
cd tiny-react
If you are using git for your version control (if not … why?) this might also be a perfect time to initialize a repository in the project directory and commit all the files so you have something to reset to if you mess something up later on.
git init
git add --all
git commit -m ‘initial commit’
Installing TinyMCE
The next step is to install TinyMCE into our project, which we simply do with npm and the following command:
npm install tinymce --save
After that has finished installing we are ready to start writing some code, so open up the project directory in your IDE/editor of choice.
Getting the skin
For the TinyMCE editor to work it needs a skin, which simply consists of font and CSS files used by the editor. The most straightforward way to get these files to the correct place in a project generated by create-react-app is to copy them from the tinymce
directory in the node_modules
to the public
directory in the root of your project (it was created by create-react-app). Anything in this public directory will be moved to the build directory by the build step provided by create-react-app, and an environment variable called PUBLIC_URL
is available to get the absolute url to the public files (the environment variable is replaced by the build step and won’t show up in the built code).
How you do the copying is up to you, either manually using the file explorer/finder, or with a terminal command looking something like this:
Macos and Linux:
cp -r node_modules/tinymce/skins public/skins
Windows
xcopy /I /E node_modules/tinymce/skins src/assets/skins
Then later, when initializing a TinyMCE instance, just add the skin_url setting with the url like this:
tinymce.init({
// other settings...
skin_url: `${process.env.PUBLIC_URL}/skins/lightgray`,
// … more settings
});
create-react-app will then take care of all the other stuff for you, making sure it works in both development and production.
Creating a simple TinyEditorComponent
Now, let’s finally get started writing some code!
Create a new directory in the src
directory called components
and create a file called TinyEditorComponent.js
in that directory.
In this file, let’s start by creating a simple component that only returns a paragraph with the text “Hello World” in it, with the code looking something like this:
import React, { Component } from "react";
class TinyEditorComponent extends Component {
render() {
return <p>Hello World</p>;
}
}
export default TinyEditorComponent;
And then add the component to the App.js
file in the source directory, changing the code to look something like this:
import React, { Component } from "react";
import logo from "./logo.svg";
import "./App.css";
import TinyEditorComponent from "./components/TinyEditorComponent";
class App extends Component {
render() {
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2>Welcome to React</h2>
</div>
<TinyEditorComponent />
</div>
);
}
}
export default App;
What we are doing here is simply importing the TinyEditorComponent
into the App.js file and then adding it to our app with the JSX syntax. If you start the development server with npm start
now you should see the text “Hello World” underneath the spinning React logo. Success! Now let’s get on with adding tinymce to the mix.
More code
I will start by adding all of the code in the TinyEditorComponent
first and then go through it bit by bit underneath.
TinyEditorComponent.js
import React, { Component } from "react";
import tinymce from "tinymce";
import "tinymce/themes/modern";
import "tinymce/plugins/wordcount";
import "tinymce/plugins/table";
class TinyEditorComponent extends Component {
constructor() {
super();
this.state = { editor: null };
}
componentDidMount() {
tinymce.init({
selector: `#${this.props.id}`,
skin_url: `${process.env.PUBLIC_URL}/skins/lightgray`,
plugins: "wordcount table",
setup: (editor) => {
this.setState({ editor });
editor.on("keyup change", () => {
const content = editor.getContent();
this.props.onEditorChange(content);
});
},
});
}
componentWillUnmount() {
tinymce.remove(this.state.editor);
}
render() {
return (
<textarea
id={this.props.id}
value={this.props.content}
onChange={(e) => console.log(e)}
/>
);
}
}
export default TinyEditorComponent;
If we start with the beginning we simply have the import statements:
import React, { Component } from "react";
import tinymce from "tinymce";
import "tinymce/themes/modern";
import "tinymce/plugins/wordcount";
import "tinymce/plugins/table";
First, we import React (and destructure out Component for a cleaner class syntax later), then tinymce. A theme is also needed for the editor to work so I import the modern theme underneath. Any plugin that you want to include will also have to be imported similarly. In this example, I am including the Word Count and Table plugins.
Next, let’s jump into the component class itself and start with the constructor:
constructor() {
super();
this.state = { editor: null };
}
Not that much to talk about here, we are only setting up the initial state to null to be as clear as possible with the fact that this component will be stateful. I think you could just skip the constructor and just set the state anyway, but let’s be as straightforward with the state as possible.
The next method is the componentDidMount
lifecycle hook:
componentDidMount() {
tinymce.init({
selector: `#${this.props.id}`,
skin_url: `${process.env.PUBLIC_URL}/skins/lightgray`,
plugins: 'wordcount table',
setup: editor => {
this.setState({ editor });
editor.on('keyup change', () => {
const content = editor.getContent();
this.props.onEditorChange(content);
});
}
});
}
Here, we will initialize the TinyMCE editor, with the selector set to the id prop that is passed down from the parent component. At this point, the skin_url
setting will be set to the public folder with the environment variable as previously explained, the two plugins that we imported are added to the plugins
setting, and the setup function setting up both states – that is, say saving a reference to the TinyMCE editor for later cleanup purposes. We are also adding the event handlers for the keyup and change events in the editor to call the onEditorChange
prop callback function passed in from the parent component with the editor content.
Next up, is the componentWillUnmount
lifecycle hook:
componentWillUnmount() {
tinymce.remove(this.state.editor);
}
This is pretty self-explanatory. It simply removes the editor when the component is unmounted (removed from the page) using the reference to the editor instance we saved in the component’s state.
Last but not least, we have the render function:
render() {
return ( <textarea id="{this.props.id}" /> );
}
This shouldn’t take that much explanation either. We simply render a textarea with the id
set to the id
prop sent in from the parent component. We also have the defaultValue
set to the ‘this.props.’ content, making it possible to initialize the editor with content in it.
If we now go back to the App.js file and change the TinyEditorComponent
JSX to add some props like this:
<TinyEditorComponent
id="myCoolEditor"
onEditorChange={(content) => console.log(content)}
/>;
Save and start up the development server again (or just wait for it to automatically reload if you left it running), and you should see a TinyMCE editor underneath the spinning React logo. Type something into the editor and you should see it being logged out in the browser console. Yay!
Wrapping up
Hopefully, you found this blog post valuable and entertaining. I also hope it is leaving you with some kind of hunger to start using TinyMCE in your future React applications. If you’re building something for a customer, consider signing up for our cloud plans to get more out of TinyMCE. If you have questions, feel free to comment below or post in our Community forum.
Have fun hacking!
Bonus material
Tired of the spinning React logo on the standard page that create-react-att generated? Open up the logo.svg file in the src directory and replace its contents with the following:
<svg viewBox="0 0 225.000000 199.000000">
<g transform="translate(0.000000,199.000000) scale(0.100000,-0.100000)" fill="#4775ce">
<path d="M551 1416 l-552 -552 31 -29 31 -29 62 62 62 62 460 -460 460 -460
559 552 558 552 -26 23 c-15 13 -32 23 -37 23 -6 0 -34 -23 -64 -51 -30 -28
-58 -49 -64 -47 -5 1 -216 206 -468 455 l-460 452 -552 -553z m984 -856 l-430
-430 -430 430 -430 430 430 430 430 430 430 -430 430 -430 -430 -430z"/>
<path d="M900 1250 l0 -40 220 0 220 0 0 40 0 40 -220 0 -220 0 0 -40z"/>
<path d="M740 1070 l0 -40 370 0 370 0 0 40 0 40 -370 0 -370 0 0 -40z"/>
<path d="M740 890 l0 -40 370 0 370 0 0 40 0 40 -370 0 -370 0 0 -40z"/>
<path d="M900 710 l0 -40 220 0 220 0 0 40 0 40 -220 0 -220 0 0 -40z"/>
</g>
</svg>
Then, a spinning TinyMCE logo appears! So much nicer! 🙂
Stuck on the App.js code?
import React, { Component } from "react";
import logo from "./logo.svg";
import "./App.css";
import TinyEditorComponent from "./components/TinyEditorComponent";
class App extends Component {
render() {
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2>Welcome to React + TinyMCE</h2>
</div>
<TinyEditorComponent
id="myCoolEditor"
onEditorChange={(content) => console.log(content)}
/>
</div>
);
}
}
export default App;