Bundling the TinyMCE .zip package with the React framework

Tiny does not recommend bundling the tinymce package. Bundling TinyMCE can be complex and error prone.

The Official TinyMCE React component integrates TinyMCE into React projects. This procedure creates a basic React application containing a TinyMCE editor.

For examples of the TinyMCE integration, visit the tinymce-react storybook.

Prerequisites

This procedure requires Node.js (and npm).

Procedure

  1. Use the Create React App package to create a new React project named tinymce-react-demo.

    npx create-react-app tinymce-react-demo
  2. Change to the newly created directory.

    cd tinymce-react-demo
  3. Eject the create-react-app so it is possible to modify the Webpack configuration.

    npm run eject

    Press 'y' when prompted.

  4. Install the tinymce-react, raw-loader and script-loader packages and save them to your package.json with --save.

    npm install --save @tinymce/tinymce-react raw-loader script-loader
  5. Unzip the content of the tinymce/js folder from the TinyMCE zip into the src folder. Afterwards the directory listing should be similar to below:

    > tree -L 2 src
    src
    ├── App.css
    ├── App.js
    ├── App.test.js
    ├── index.css
    ├── index.js
    ├── logo.svg
    ├── reportWebVitals.js
    ├── setupTests.js
    └── tinymce
        ├── icons
        ├── langs
        ├── license.txt
        ├── models
        ├── plugins
        ├── skins
        ├── themes
        ├── tinymce.d.ts
        └── tinymce.min.js
  6. Using a text editor, open ./config/paths.js, after the line with appSrc add the line:

     appTinymce: resolveApp('src/tinymce'),
    Diff of ./config/paths.js
    diff --git a/config/paths.js b/config/paths.js
    index f0a6cd9..a0d2f50 100644
    --- a/config/paths.js
    +++ b/config/paths.js
    @@ -60,6 +60,7 @@ module.exports = {
       appIndexJs: resolveModule(resolveApp, 'src/index'),
       appPackageJson: resolveApp('package.json'),
       appSrc: resolveApp('src'),
    +  appTinymce: resolveApp('src/tinymce'),
       appTsConfig: resolveApp('tsconfig.json'),
       appJsConfig: resolveApp('jsconfig.json'),
       yarnLockFile: resolveApp('yarn.lock'),
  7. Using a text editor, open ./config/webpack.config.js and make the following edits:

    • Find the ModuleScopePlugin and add require.resolve('script-loader'), to its array of exceptions.

    • Find the rule for processing javascript in the src directory and add the new rule above it:

      {
        test: /\.(js)$/,
        include: paths.appTinymce,
        loader: require.resolve('script-loader'),
      },
    • Find the ESLintPlugin and add exclude: ["tinymce"], to its options.

    Diff of ./config/webpack.config.js
    diff --git a/config/webpack.config.js b/config/webpack.config.js
    index 6b4a4cd..e0d1952 100644
    --- a/config/webpack.config.js
    +++ b/config/webpack.config.js
    @@ -331,6 +331,7 @@ module.exports = function (webpackEnv) {
               babelRuntimeEntry,
               babelRuntimeEntryHelpers,
               babelRuntimeRegenerator,
    +          require.resolve('script-loader'),
             ]),
           ],
         },
    @@ -399,6 +400,11 @@ module.exports = function (webpackEnv) {
                     and: [/\.(ts|tsx|js|jsx|md|mdx)$/],
                   },
                 },
    +            {
    +              test: /\.(js)$/,
    +              include: paths.appTinymce,
    +              loader: require.resolve('script-loader'),
    +            },
                 // Process application JS with Babel.
                 // The preset includes JSX, Flow, TypeScript, and some ESnext features.
                 {
    @@ -724,6 +730,7 @@ module.exports = function (webpackEnv) {
             new ESLintPlugin({
               // Plugin options
               extensions: ['js', 'mjs', 'jsx', 'ts', 'tsx'],
    +          exclude: ["tinymce"],
               formatter: require.resolve('react-dev-utils/eslintFormatter'),
               eslintPath: require.resolve('eslint'),
               failOnError: !(isEnvDevelopment && emitErrorsAsWarnings),
  8. Using a text editor, create ./src/BundledEditor.js and set the contents to:

    import { Editor } from '@tinymce/tinymce-react';
    
    // TinyMCE so the global var exists
    // eslint-disable-next-line no-unused-vars
    import './tinymce/tinymce.min.js';
    // DOM model
    import './tinymce/models/dom/model.min.js'
    // Theme
    import './tinymce/themes/silver/theme.min.js';
    // Toolbar icons
    import './tinymce/icons/default/icons.min.js';
    // Editor styles
    import './tinymce/skins/ui/oxide/skin.min.css';
    
    // importing the plugin js.
    // if you use a plugin that is not listed here the editor will fail to load
    import './tinymce/plugins/advlist/plugin.min.js';
    import './tinymce/plugins/anchor/plugin.min.js';
    import './tinymce/plugins/autolink/plugin.min.js';
    import './tinymce/plugins/autoresize/plugin.min.js';
    import './tinymce/plugins/autosave/plugin.min.js';
    import './tinymce/plugins/charmap/plugin.min.js';
    import './tinymce/plugins/code/plugin.min.js';
    import './tinymce/plugins/codesample/plugin.min.js';
    import './tinymce/plugins/directionality/plugin.min.js';
    import './tinymce/plugins/emoticons/plugin.min.js';
    import './tinymce/plugins/fullscreen/plugin.min.js';
    import './tinymce/plugins/help/plugin.min.js';
    import './tinymce/plugins/image/plugin.min.js';
    import './tinymce/plugins/importcss/plugin.min.js';
    import './tinymce/plugins/insertdatetime/plugin.min.js';
    import './tinymce/plugins/link/plugin.min.js';
    import './tinymce/plugins/lists/plugin.min.js';
    import './tinymce/plugins/media/plugin.min.js';
    import './tinymce/plugins/nonbreaking/plugin.min.js';
    import './tinymce/plugins/pagebreak/plugin.min.js';
    import './tinymce/plugins/preview/plugin.min.js';
    import './tinymce/plugins/quickbars/plugin.min.js';
    import './tinymce/plugins/save/plugin.min.js';
    import './tinymce/plugins/searchreplace/plugin.min.js';
    import './tinymce/plugins/table/plugin.min.js';
    import './tinymce/plugins/template/plugin.min.js';
    import './tinymce/plugins/visualblocks/plugin.min.js';
    import './tinymce/plugins/visualchars/plugin.min.js';
    import './tinymce/plugins/wordcount/plugin.min.js';
    
    // importing plugin resources
    import './tinymce/plugins/emoticons/js/emojis.js';
    
    // Content styles, including inline UI like fake cursors
    /* eslint import/no-webpack-loader-syntax: off */
    import contentCss from '!!raw-loader!./tinymce/skins/content/default/content.min.css';
    import contentUiCss from '!!raw-loader!./tinymce/skins/ui/oxide/content.min.css';
    
    export default function BundledEditor(props) {
      const {init, ...rest} = props;
      // note that skin and content_css is disabled to avoid the normal
      // loading process and is instead loaded as a string via content_style
      return (
        <Editor
          init={{
            ...init,
            skin: false,
            content_css: false,
            content_style: [contentCss, contentUiCss, init.content_style || ''].join('\n'),
          }}
          {...rest}
        />
      );
    }
  9. Using a text editor, open ./src/App.js and replace the contents with:

    import React, { useRef } from 'react';
    import BundledEditor from './BundledEditor'
    
    export default function App() {
      const editorRef = useRef(null);
      const log = () => {
        if (editorRef.current) {
          console.log(editorRef.current.getContent());
        }
      };
      return (
        <>
          <BundledEditor
            onInit={(evt, editor) => editorRef.current = editor}
            initialValue='<p>This is the initial content of the editor.</p>'
            init={{
              height: 500,
              menubar: false,
              plugins: [
                'advlist', 'anchor', 'autolink', 'help', 'image', 'link', 'lists',
                'searchreplace', 'table', 'wordcount'
              ],
              toolbar: 'undo redo | blocks | ' +
                'bold italic forecolor | alignleft aligncenter ' +
                'alignright alignjustify | bullist numlist outdent indent | ' +
                'removeformat | help',
              content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }'
            }}
          />
          <button onClick={log}>Log editor content</button>
        </>
      );
    }
  10. Test the application using the Node.js development server.

    • To start the development server, navigate to the tinymce-react-demo directory and run:

      npm run start
    • To stop the development server, select on the command line or command prompt and press Ctrl+C.

Deploying the application to a HTTP server

The application will require further configuration before it can be deployed to a production environment. For information on configuring the application for deployment, see: Create React App - Deployment.

To deploy the application to a local HTTP Server:

  1. Navigate to the tinymce-react-demo directory and run:

    npm run build
  2. Copy the contents of the tinymce-react-demo/build directory to the root directory of the web server.

The application has now been deployed on the web server.

Additional configuration is required to deploy the application outside the web server root directory, such as http://localhost:<port>/my_react_application.

Next Steps