Setting up TinyMCE in your React app
Tags: react, wysiwyg, api
Published: 2023-05-16 20:48:48

One of the features I was most curious about learning how to set up was a WYSIWYG editor. They can really help bring text content to life and take your application to that next level.

After doing a bit of research, I landed on TinyMCE for my blog app. Not only did they provide [great support](https://www.tiny.cloud/docs/integrations/react/), but they also offered a free version! Let's jump right in, shall we?

**Preliminary requirements**

- Node.js (I am using v16.4)
- You're working on a React app! (run $ npx create-react-app client --use-npm in your command line if you haven't started one yet)
- npm install --save @tinymce/tinymce-react

**Basic setup**

- You'll want to add the following code to import the TinyMCE editor to your App. The following is an example from my own app:

```javascript
import React, { useRef } from 'react';
import { Editor } from '@tinymce/tinymce-react';
const TextEditor = ({ formData, setFormData }) => {
  const editorRef = useRef(null);

  const handleEditorChange = (content, editor) => {
    setFormData({ ...formData, description: content });
  }

  return (
    <>
      <Editor
        onInit={(evt, editor) => editorRef.current = editor}
        // there's a known issue with how tinymce works where the intialValue and value
        // come into conflict when using useState. tinymce recommend removing initialValue
        // and setting the initial value as the the default state value i.e. formData.description
        // is set to the placeholder text instead of just an empty string
        // initialValue="<p>This is the initial content of the editor.</p>"
        apiKey={process.env.REACT_APP_TINY_MCE_API_KEY}
        init={{
          height: 500,
          menubar: true,
          plugins: 'advlist autolink lists link image charmap preview anchor ' +
            'searchreplace visualblocks code fullscreen ' +
            'insertdatetime media table code help wordcount'
          ,
          toolbar: 'undo redo | formatselect | ' +
          'bold italic backcolor | alignleft aligncenter ' +
          'alignright alignjustify | bullist numlist outdent indent | ' +
          'removeformat | help',
          content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }'
        }}
        value={formData.description}
        onEditorChange={handleEditorChange}
      />
      {/* <button onClick={log}>Log editor content</button> */}
    </>
  );
}

export default TextEditor
```

I created a separate re-usable component for the TinyMCE editor because I planned on using it on a "draft" page and on an "edit" page, which had their own distinct stateful variables.

**Generate an API key and set up a .env file to store it**

There's a lot going on there, but the most important part to highlight is the "apiKey" property. TinyMCE allows setting up the editor without an API. However, doing so would mean that you'd have a pop-up message stating that your "domain is not recognized". While that message could be hidden with some CSS, I opted to sign up for TinyMCE to use their free version, which still provides you with your own API key.

If you're a newcomer like me, you should know that it's bad practice to place your API keys in parts of your application that you plan on pushing to a remote repository. This is because you wouldn't want anyone to have the keys to your app. If you're opting to use an API key, then you'll want to create a .env file inside to properly store your credentials. In this case, it should go inside the client folder (e.g. client > .env). 

.env files are meant to be stored locally and not be uploaded to code repositories online for everyone to read. This is the ideal place to store any credentials needed by your application in a key-value format. 

Once you've set up your .env file and generated your TinyMCE API key, you can define the credentials like so:

```
client > .env

REACT_APP_TINY_MCE_API_KEY=yourAPIkeygoesherewithoutanyquotes
```

React .env naming conventions require that the variable key starts with REACT_APP. You could name it anything, like "REACT_APP_TEXT_EDITOR", but you'll just want to make sure it starts with "REACT_APP".

Lastly, make sure you add your .env file to the .gitignore file, if it isn't there already. The .gitignore file will ensure that .env isn't pushed to a remote repository, so your keys will remain secure.

In order to access the key within the application, you'll need to use a method specific to Node:

```
process.env.REACT_APP_TINY_MCE_API_KEY
```
The TinyMCE component has an apiKey property where we can input this like so:

```
 apiKey={process.env.REACT_APP_TINY_MCE_API_KEY}
```
That's it as far as setting up your component and API in your local environment! 

**BONUS: Using react-html-parser to display TinyMCE HTML content in other components**

The above example is specific to my app and that particular component's state. In my case, I've got a formData object in state which ensures I'm displaying whatever is being typed into the text editor.

When I click submit on my form, I'm then taking that formData and sending it in a POST request to my backend. Any edits made within the TextEditor field such as font or style changes, colors, uploaded images, will be sent as HTMl to the backend.

For instance, this is how a blog post description is being stored in my database after submitting the form with text edited with TinyMCE:

```html
"<p><span style=\"background-color: rgb(251, 238, 184);\">We had a lovely day!</span> Went for a bike ride, then checked out the new exhibit at <strong>NCMA</strong>!</p>\n<p>&nbsp;</p>"
```

The great thing is that TinyMCE can display the HTML correctly when it is fetched from the database if say, you want to allow users to make edits to the content (i.e. editing a blog post).

If you wish to show that description in any other context, for example within another component that doesn't have the TinyMCE editor, then you would need to convert it so that it correctly renders the styled text. In comes React HTML Parser!

[React HTML Parser](https://www.npmjs.com/package/react-html-parser) is "A utility for converting HTML strings into React components. Avoids the use of dangerouslySetInnerHTML and converts standard HTML elements, attributes and inline styles into their React equivalents." Here's how to install it:

`npm install react-html-parser`

Perfect! 

You can then import React HTML Parser's "parse" method in any component where you expect the need to convert the TinyMCE HTML to be properly rendered by React. For instance, In my published blog post component, I imported the following:

```
import parse from 'html-react-parser';
```

Then I defined a variable for the rendered blog post:

```
const renderedDescription = parse(`${blog_post.description}`)
```
Then I placed the variable inside the component where I wanted it to render:

```html
<Box xs={12} sx={{ mt: '1rem', mb: 'rem' }}>
     <Typography variant='body'>{renderedDescription}</Typography>
</Box>
```
That should be it!

**BONUS: Displaying the HTML content as a regular string**

What if you want to display the non-styled content in another component? For example, in a small card in your home or search pages.

There are probably a few things you can try, but in my case, I went with regex:

```javascript
 const htmlString = `${blog_post.description}`;
  const plainString = htmlString.replace(/(<([^>]+)>)/ig, '');
```
 I then placed the "plainString" variable inside the React component like so:

```javascript
        <Typography variant="body2" color="text.secondary">
          {plainString.slice(0, 50) + '...'}
        </Typography>
```

**BONUS: Deploying your app with TinyMCE**

Last but not least, if you opt to deploy your application, you'll want to make sure you're setting up the API key correctly according to the service you're using. 

In my case, I used Render, which meant I had to go into my app's corresponding "Web service" > Environment > enter my API variable in the "Environment Variables" section.

Remember to name the variable the exact same way as you did in your .env file when setting up the API locally.

That was a LOT, but I hope it was helpful in setting up what I consider to be an awesome HTML editor in your application!