Arsalan Khattak
24 June 2025

Using the TinyMCE rich text editor in Bryntum Grid

TinyMCE is a popular WYSIWYG rich text editor that can be used to create a Microsoft Word-like editing experience in […]

TinyMCE is a popular WYSIWYG rich text editor that can be used to create a Microsoft Word-like editing experience in web apps. The basic open-source version of the editor is available for free. You can enhance its capabilities by paying for a TinyMCE subscription and optional add-on features, such as an AI assistant, support for math equations, and revision history.

In this tutorial, we’ll show you how to use the TinyMCE rich text editor in a Bryntum Grid, which is a high-performance, feature-rich, and customizable JavaScript table component.

You’ll learn how to:

You can find the code for the completed tutorial on the completed-grid branch of this GitHub repository.

Getting started

Clone the starter GitHub repository. This starter repository uses the development server and JavaScript bundler, Vite. You need Node.js version 18+ for Vite to work.

Install the dependencies by running the following command:

npm install

Then, run the local dev server as follows:

npm run dev

Finally, open http://localhost:5173.

You should see a blank white screen.

The src/public folder has two example data files that contain information about people who are interested in food:

Creating a Bryntum Grid

Let’s start by creating a Bryntum Grid to display the Simple Foodies data. We’ll install the Bryntum Grid, configure it, and style it.

Installing Bryntum Grid

Before you can install the Bryntum Grid component, you first need to access and log in to the Bryntum npm registry.

Bryntum components are licensed commercial products, but you can use the free trial version of Bryntum Grid for this tutorial.

If you’re installing the trial version, run the following command:

npm install @bryntum/grid@npm:@bryntum/grid-trial@6.3.0-alpha-1

If you have a Bryntum Grid license, install the component as follows:

npm install @bryntum/grid@6.3.0-alpha-1

We’re using the 6.3.0-alpha-1 version of Bryntum Grid for this tutorial because it includes the RichTextField widget that we’ll use to create a custom TinyMCE input field.

Configuring and rendering the Bryntum Grid

Replace the console log in the src/main.js file with the following lines of code:

import { Grid } from '@bryntum/grid';

const grid = new Grid({
    appendTo : 'container',
    columns  : [
        {
            text  : 'Name',
            field : 'name',
            flex  : 1
        }, {
            text     : 'Email',
            field    : 'email',
            type     : 'template',
            template : ({ value }) => `<a href="mailto:${value}">${value}</a>`,
            flex     : 1
        }, {
            text  : 'City',
            field : 'city',
            flex  : 1
        }, {
            text     : 'Description',
            field    : 'description',
            flex     : 2,
            minWidth : 334
        },
        {
            text   : 'Date',
            field  : 'date',
            type   : 'date',
            format : 'YYYY-MM-DD',
            flex   : 1
        },
        {
            text  : 'Color',
            field : 'color',
            type  : 'color',
            width : 80
        }
    ],

    store : {
        readUrl  : 'foodiesSimple.json',
        autoLoad : true
    }
});

Here, we create an instance of the Bryntum Grid and set the following configuration properties:

Styling the Bryntum Grid

Now that we have a component, we can style it.

By default, the Grid component is configured to occupy 100% of the parent DOM element and has a min-height of 10em.

In the src/styles.css file, make the grid take up the full height of the screen by adding the following styles to its parent div element:

#container {
    margin         : 0;
    display        : flex;
    flex-direction : column;
    height         : 100vh;
    font-size      : 14px;
}

Import the Bryntum Grid Stockholm theme by adding the following line of code to the top of the src/styles.css file:

@import "../node_modules/@bryntum/grid/grid.stockholm.css";

The Stockholm theme is one of five available themes for Bryntum Grid. If you want to create a custom theme or use multiple themes, check out the Bryntum Grid Styling documentation.

Run the local dev server using npm run dev. You’ll see a Bryntum Grid component displaying the Simple Foodies data:

Adding TinyMCE to your app

Now, let’s create a custom TinyMCE input field that we can use as a custom editor for cells in the Description column. This will allow us to format and add images to the description text.

To use TinyMCE, you first need to create an account. You can sign up for a free account, which includes a 14-day trial of advanced features. No credit card details are required.

Once you’ve logged in, the Integrate TinyMCE page will open in your account dashboard. You can install TinyMCE using Tiny Cloud, package managers, or self-hosted downloads. We’ll use Tiny Cloud for this guide.

Copy the API key by clicking the Get your API key button:

In the src/index.html file in your Bryntum Grid app, add the following script below the div with the id="container":

<script src="https://cdn.tiny.cloud/1/your-api-key/tinymce/7/tinymce.min.js" referrerpolicy="origin"></script>

To load the script, TinyMCE needs at least one approved root domain. By default, localhost is an approved domain. When you deploy your app, add the domain using the Approved Domains page in your Tiny Cloud dashboard.

Note that an API key is used when loading TinyMCE from the Tiny Cloud. Use a license key when self-hosting TinyMCE.

Creating a custom TinyMCE input field widget

Bryntum components have UI elements called widgets that you can add to your project interface. These widgets, such as Button, DatePicker, and RichTextField, are instances of classes that extend the base Widget class.

The RichTextField widget is an interface class designed to be used with rich text field editors like TinyMCE. It uses a div as the input field, which rich text editors use as their target element. However, RichTextField isn’t used directly. Instead, we’ll use it as a base class to create a rich text editor-specific class.

Let’s create a TinyMCE input field widget.

Create a lib folder in the src folder, then create a TinyMceField.js file within src/lib and add the following lines of code to it:

import { DomHelper, GlobalEvents, RichTextField } from '@bryntum/grid';

export default class TinyMceField extends RichTextField {
    static $name = 'TinyMceField';
    static type = 'tinymcefield';

    tinymce = null;

}

TinyMceField.initClass();

In this code, the TinyMceField class extends the RichTextField class. We give the widget its own $name and type, and create a property that will hold the TinyMCE instance. The initClass() method registers the widget with the base Widget class, which allows us to create the TinyMceField widget by type.

Next, add the following static configurable property below the tinymce property, making sure to replace 'your-api-key' with your TinyMCE API key:

static configurable = {
    tinyMceConfig : {},
    apiKey : 'your-api-key',
    inline : false,
    resize : false,
    menubar : false,
    autoFocus : true,
    rootBlock : 'div',
    inputAttributes : {
      tag : 'textarea'
    }
};

This configuration disables widget resizing and hides the drop-down menu bar.

When the widget is made visible, we’ll populate the tinyMceConfig using the default iframe-based TinyMCE editor. You can experiment with this inline version of the editor in the Bryntum Grid TinyMCE Cell editor demo.

Add the following _onPaint method below the configurable property:

_onPaint() {
    const me = this;

    if (me.editor) {
        me.destroyEditor();
    }

    if (!me.editor) {
        const html = me.value ?? '';
        me.input.value = html;
  
        globalThis.tinymce.init({
            ...me.tinyMceConfig,
            apiKey       : me.apiKey,
            auto_focus        : me.autoFocus,
            inline            : me.inline,
            forced_root_block : me.rootBlock,
            menubar           : me.menubar,
            resize            : me.resize,
            height            : me.height,
            target            : me.input,
            skin              : DomHelper.themeInfo?.name
                ?.toLowerCase().endsWith('-dark')
                ? 'oxide-dark'
                : 'oxide',
            ui_mode : 'split',
  
            setup : editor => {
                editor.on('init', () => editor.setContent(html, { format : 'html' }));
                
                editor.on('NodeChange', () => {
                    if (me.isDestroying) return;
  
                    const newVal = editor.getContent();
                    if (newVal !== me.value) {
                        me.richText = newVal;
                        me.triggerFieldChange({
                            value      : newVal,
                            oldValue   : me.value,
                            userAction : true
                        });
                    }
                });
  
                editor.on('blur', () => {
                    const grid = me.up('grid');
                    grid.finishEditing();
                });
            }
        }).then(editors => (me.editor = editors[0]));
    }
}

The onPaint method is called when the widget or an element in the widget becomes visible. The _onPaint above is called when the TinyMCE cell editor is opened in our Bryntum Grid:

Now add the following owns method below the _onPaint method:

owns(target) {
    return super.owns(target) || Boolean(target?.closest('.tox-tinymce'));
}

The owns method returns true if the widget owns the passed-in element, event, or widget. We use it to indicate that the TinyMCE floating toolbar element is part of this widget.

Add the following destroyEditor method below the owns method:

destroyEditor() {
    this.editor?.destroy(); // Destroy the existing instance
    this.editor = null;
}

We use this method in _onPaint to destroy the editor object.

Lastly, add the following construct method below the configurable property:

construct(config = {}) {
    super.construct(config);

    const me = this;

    GlobalEvents.ion({
        theme   : 'destroyEditor',
        thisObj : me
    });

   me.up(w => w.isPopup)?.ion({
        hide    : 'destroyEditor',
        thisObj : me
    });

    me.ion({
        paint   : '_onPaint',
        thisObj : me
    });
}

The construct method applies the configuration. When the global theme changes or a surrounding popup is hidden, we call destroyEditor, and on every paint, we rerun _onPaint.

Adding the TinyMCE editor to the Description column

In the src/main.js file, import your custom TinyMCE input field widget:

import './lib/TinyMceField.js';

Add the following properties to the 'Description' column object in the Grid columns config array:

type               : 'template',
template           : ({ value }) => value,
autoHeight         : true,
revertOnEscape     : false,
managedCellEditing : false,
cellEditor         : {
    // The rich text editor is allowed to overflow the cell height.
    matchSize : {
        height : false
    }
},
editor : {
    type   : 'tinymcefield',
    inline : false,
    height : 400
}

We change the column type to template, allowing us to use a template for the cell content, so we can render rich HTML content, like image tags, in the cell. We use the editor config object to create the input field for editing cells in the Description column and set its type to the TinyMCE input field.

In the store config, replace the Simple Foodies data with the rich text data in the Foodies JSON file by changing the readUrl as follows:

- readUrl : 'foodiesSimple.json',
+ readUrl : 'foodies.json',

Add the following styles to the src/styles.css file:

.b-grid-cell[data-column="description"] {
  display       : block;
  padding-block : .75em;

  h3 {
      margin-block : 0 .5em;
  }
}

.b-tinymcefield {
    .tox.tox-tinymce {
        width         : 100%;
        border-radius : 2px;

        &:not(.tox-tinymce-inline) {
            border : 1px solid #d0d1d2;
        }

        &.tox-edit-focus {
            border-color : rgba(254, 172, 49, 0.6);
        }

        .tox-edit-area::before {
            border : none;
        }
    }
}

When you run the dev server, you’ll now see rich text rendered in the Description column. You can format the text of the Description cells and add images to them.

Next steps

Take a look at the TinyMCE editing demo on the Tiny Cloud homepage to see the advanced features that you can add to your editor, such as the Microsoft Word import and export functionality.

Visit the Bryntum Grid demo page to see some of the other features you can add to your component, such as:

Arsalan Khattak

Bryntum Grid