Dynamic loading of TinyMCE

Amit Joki
3 min readJul 14, 2020

--

TinyMCE has been the de-facto standard for web based WYSIWYG (What You See Is What You Get) for quite sometime now.

But does it do justice to its name? Not really. TinyMCE isn’t tiny anymore. If you use TinyMCE in your websites, it will be among one of the larger resources you’ll be loading into your clients’ browsers.

We at WikiEduDashboard have been using TinyMCE as well. My project with them this summer, under the program of Google Summer of Code was to reduce the bundle size.

My earlier articles have spoken about the steps I took to reduce the bundle size. This week, I turned my attention towards how to reduce TinyMCE’s footprint.

Now, there are two ways you can go about this:

  1. You could replace TinyMCE with leaner and sleeker alternatives like Pell or a slightly more sophisticated, but nevertheless leaner, QuillJS.
  2. Or if you do want to use TinyMCE and still want to improve the page loading experience, you could load it dynamically only when it is needed.

For us, the second way was more optimum. TinyMCE handled some edge cases well so we didn’t really want to migrate to other libraries, but we still wanted to improve the page loading experience.

Upon looking at the codebase, TinyMCE was only ever when the user is logged in and is an instructor at WikiEduDashboard.

So the percentage of people who actually needed TinyMCE was trivial. There was no point in loading TinyMCE for nearly ~95% of the users at all.

So, we needed to load TinyMCE only when it was needed. How did I go about that? Let us see presently.

First, we already had a Webpack entry point called tinymce which loaded TinyMCE dependencies — TinyMCE itself + skins.

We now simply needed to load it dynamically. This was done by inserting <script id="tiny-mce-script"> tag into the head.

Note the ID part. It is important so we can check if we have already loaded TinyMCE or not. If the script tag with the ID tiny-mce-scriptis present in the document, it means TinyMCE is already loaded.

Okay enough talking, here’s some code.

loadTinyMCE() {
const tinyMCEPath = '/path/to/tinymce/bundle';
const script = document.createElement('script');
script.id = 'tiny-mce-script';
script.onload = () => {
// tinymce is loaded at this point
this.setState({
tinymceLoaded: true
});
};
script.src = tinyMCEPath;
document.head.appendChild(script);
}

So you can see we have a component state called tinymceLoaded which keeps track of whether the TinyMCE is loaded or not.

import Editor from 'tinymce-react';
let element = <textarea />
if (this.state.tinymceLoaded) {
// use <Editor /> from tinymce-react here
element = <Editor />
}

The above snippet is how you would use <textarea> as a fallback until the TinyMCE is loaded.

Now you might have a question of when to use the loadTinyMCE function. This is a good chance to use componentDidUpdate() lifecycle of React.

componentDidMount() {
// check if tinymce is already loaded
const tinyMCEScript = document.getElementById('tiny-mce-script');
if (tinyMCEScript !== null) {
this.setState({
tinymceLoaded: true
});
return;
}
this.loadTinyMCE(); // at this point, we can be sure it isn't loaded, so load it.
}

The code snippets have been dumbed down to include only essential bits needed to get the gist. The full implementation can be found in this pull request — #4101.

--

--