TinyMCE
Project types | webemail |
Plan | Startup plan |
Replace the default RTE with TinyMCE 6 (latest MIT).

Install the Studio SDK plugins package:
- npm
- pnpm
- yarn
- CDN
npm i @grapesjs/studio-sdk-plugins
pnpm add @grapesjs/studio-sdk-plugins
yarn add @grapesjs/studio-sdk-plugins
// It's recommended to replace the `latest` tag with the specific latest version to avoid any potential breaking changes.
// The loaded plugin is globally available via `globalThis.StudioSdkPlugins_rteTinyMce`.
"https://unpkg.com/@grapesjs/studio-sdk-plugins@latest/dist/rteTinyMce/index.umd.js"
Import and use the plugin in your project:
- React
- JS
- 🍇 Demo
import StudioEditor from '@grapesjs/studio-sdk/react';
import '@grapesjs/studio-sdk/style';
import { rteTinyMce } from '@grapesjs/studio-sdk-plugins';
// ...
<StudioEditor
options={{
// ...
plugins: [
rteTinyMce.init({
enableOnClick: true,
// Custom TinyMCE configuration
loadConfig: ({ component, config }) => {
const demoRte = component.get('demorte');
if (demoRte === 'fixed') {
return {
toolbar:
'bold italic underline strikethrough | alignleft aligncenter alignright alignjustify | link image media',
fixed_toolbar_container_target: document.querySelector('.rteContainer')
};
} else if (demoRte === 'quickbar') {
return {
plugins: `${config.plugins} quickbars`,
toolbar: false,
quickbars_selection_toolbar: 'bold italic underline strikethrough | quicklink image'
};
}
return {};
}
})
],
layout: {
default: {
type: 'row',
style: { height: '100%' },
children: [
{ type: 'sidebarLeft' },
{
type: 'column',
style: { flexGrow: 1 },
children: [
{ type: 'sidebarTop' },
{ type: 'canvas' },
// Empty container for the fixed RTE toolbar
{ type: 'row', className: 'rteContainer', style: { justifyContent: 'center' } }
]
},
{ type: 'sidebarRight' }
]
},
},
project: {
default: {
pages: [
{
name: 'Home',
component: `
<div data-gjs-type="text">
<h1>Default configuration</h1>
Text content start
<div>
Block content with <em>emphasis</em>, <b>bold text</b>, <a href="#some-link" target="_blank">link</a>.
</div>
<ul>
<li>List item 1</li>
<li>List item 2</li>
</ul>
<ol>
<li>Ordered list item 1</li>
<li>Ordered list item 1</li>
</ol>
Text content end
</div>
<div data-gjs-type="text" data-gjs-demorte="fixed">
<h1>Fixed position</h1>
Text content start
<div>
Block content with <em>emphasis</em>, <b>bold text</b>, <a href="#some-link" target="_blank">link</a>.
</div>
<ul>
<li>List item 1</li>
<li>List item 2</li>
</ul>
<ol>
<li>Ordered list item 1</li>
<li>Ordered list item 1</li>
</ol>
Text content end
</div>
<div data-gjs-type="text" data-gjs-demorte="quickbar">
<h1>Quickbar</h1>
Text content start
<div>
Block content with <em>emphasis</em>, <b>bold text</b>, <a href="#some-link" target="_blank">link</a>.
</div>
<ul>
<li>List item 1</li>
<li>List item 2</li>
</ul>
<ol>
<li>Ordered list item 1</li>
<li>Ordered list item 1</li>
</ol>
Text content end
</div>
`
},
]
},
}
}}
/>
import createStudioEditor from '@grapesjs/studio-sdk';
import '@grapesjs/studio-sdk/style';
import { rteTinyMce } from '@grapesjs/studio-sdk-plugins';
// ...
createStudioEditor({
// ...
plugins: [
rteTinyMce.init({
enableOnClick: true,
// Custom TinyMCE configuration
loadConfig: ({ component, config }) => {
const demoRte = component.get('demorte');
if (demoRte === 'fixed') {
return {
toolbar:
'bold italic underline strikethrough | alignleft aligncenter alignright alignjustify | link image media',
fixed_toolbar_container_target: document.querySelector('.rteContainer')
};
} else if (demoRte === 'quickbar') {
return {
plugins: `${config.plugins} quickbars`,
toolbar: false,
quickbars_selection_toolbar: 'bold italic underline strikethrough | quicklink image'
};
}
return {};
}
})
],
layout: {
default: {
type: 'row',
style: { height: '100%' },
children: [
{ type: 'sidebarLeft' },
{
type: 'column',
style: { flexGrow: 1 },
children: [
{ type: 'sidebarTop' },
{ type: 'canvas' },
// Empty container for the fixed RTE toolbar
{ type: 'row', className: 'rteContainer', style: { justifyContent: 'center' } }
]
},
{ type: 'sidebarRight' }
]
},
},
project: {
default: {
pages: [
{
name: 'Home',
component: `
<div data-gjs-type="text">
<h1>Default configuration</h1>
Text content start
<div>
Block content with <em>emphasis</em>, <b>bold text</b>, <a href="#some-link" target="_blank">link</a>.
</div>
<ul>
<li>List item 1</li>
<li>List item 2</li>
</ul>
<ol>
<li>Ordered list item 1</li>
<li>Ordered list item 1</li>
</ol>
Text content end
</div>
<div data-gjs-type="text" data-gjs-demorte="fixed">
<h1>Fixed position</h1>
Text content start
<div>
Block content with <em>emphasis</em>, <b>bold text</b>, <a href="#some-link" target="_blank">link</a>.
</div>
<ul>
<li>List item 1</li>
<li>List item 2</li>
</ul>
<ol>
<li>Ordered list item 1</li>
<li>Ordered list item 1</li>
</ol>
Text content end
</div>
<div data-gjs-type="text" data-gjs-demorte="quickbar">
<h1>Quickbar</h1>
Text content start
<div>
Block content with <em>emphasis</em>, <b>bold text</b>, <a href="#some-link" target="_blank">link</a>.
</div>
<ul>
<li>List item 1</li>
<li>List item 2</li>
</ul>
<ol>
<li>Ordered list item 1</li>
<li>Ordered list item 1</li>
</ol>
Text content end
</div>
`
},
]
},
}
})
Plugin options
| Property | Type | Description |
|---|---|---|
licenseKey | string | The license key for the plugin. This is optional, only required if the plugin is used outside of Studio SDK. Example |
cdnScript | string | CDN scripts to load dynamically in case the library is not available. Default |
enableOnClick | boolean | Enable RTE on click of the text component, instead of the default double-click. Default |
skipCustomTheme | boolean | Skip adding custom CSS styles for the toolbar based on the Studio theme. Default |
loadConfig | function | Pass your custom configuration to the TinyMCE editor. Example |