With millions of developers worldwide relying on TinyMCE as their go-to rich text editor, we remain committed to delivering the most reliable and feature-rich editing experience available. In early 2024, we introduced TinyMCE version 7, building on our previous releases with significant updates and improvements.
While the basic setup process remains the same as in TinyMCE 6, several configuration changes affect complex use cases, including: lists, accessibility, tables, custom plugins, media embeddings, and security. Additionally, we have introduced Markdown support, document converters like Import from MS Word and Export to PDF, and revision history plugins as part of the TinyMCE 7 release. Furthermore, in May 2024, TinyMCE 7.1 brought the addition of Math plugins.
Ordered and Unordered Lists have been removed from Tiny Core
Before the change in TinyMCE 7.0, the InsertOrderedList and InsertUnorderedList commands were part of the core functionality. This allowed users to create ordered and unordered lists without a separate plugin.
Here is an example of how TinyMCE was initialized before version 7.0, without explicitly including the Lists plugin:
tinymce.init({
selector: "textarea",
toolbar: "bullist numlist",
});
Since the list commands were part of the core, they worked without additional plugins. However, this sometimes resulted in inconsistent behavior when users tried to create lists using text patterns like “1.” or “-.”
Starting with TinyMCE 7, these list commands are no longer part of the core editor. Instead, they are now included exclusively within the Lists plugin. This means that to use the list functionality (ordered and unordered lists), you must explicitly include the Lists plugin in your TinyMCE configuration. Here’s an example:
tinymce.init({
selector: "textarea",
plugins: ["lists"],
toolbar: "bullist numlist",
});
Remove Trailing Line Breaks has been removed from TinyMCE’s DomParser
The remove_trailing_brs property was a setting within TinyMCE's DomParser API that controlled whether trailing <br> tags (line breaks) at the end of the content or blocks should be removed during parsing. This setting was deprecated in TinyMCE version 6.5. In TinyMCE 7.0, this has been completely removed.
Introducing the ‘trigger’ property for Text Patterns
Text patterns let users type shortcuts to format text quickly. For example, typing # at the beginning of a line and pressing Enter might turn that line into a heading. TinyMCE 7.0 introduced a new "trigger" property. This property allows text patterns to be activated by either pressing the Space or Enter key.
text_patterns: [
{ start: "#", format: "h1", trigger: "space" }, // # followed by space becomes a H1
{ start: "##", format: "h2", trigger: "space" }, // ## followed by space becomes a H2
];
If you prefer the old behavior in Tiny 6 where you activate the patterns by pressing Enter, you can customize the configuration like this:
text_patterns: [
{ start: "#", format: "h1", trigger: "enter" }, // # followed by space becomes a H1
{ start: "##", format: "h2", trigger: "enter" }, // ## followed by space becomes a H2
];
This new property makes TinyMCE’s text patterns more flexible and user-friendly, allowing you to choose how you want to activate these text formatting shortcuts.
Autocompleter ‘trigger’ property is here to stay
Previously, the autocompleter used a property called ch to specify which single character would trigger the suggestions. For example:
tinymce.init({
selector: "#editor",
plugins: "...",
toolbar: "...",
editable_root: false,
editable_class: "editable",
setup: (editor) => {
editor.ui.registry.addAutocompleter("example", {
ch: "@", // '@' triggers the autocompleter
fetch: function (pattern) {
// Fetch and return autocomplete suggestions based on the pattern
},
});
},
});
The ch property has been deprecated since TinyMCE v6.2, meaning it has been phased out and will now be removed in Tiny 7.
Instead, you should use the trigger property. The trigger property is more flexible because it allows you to specify one or more characters as the trigger. Here’s what that would look like:
tinymce.init({
selector: "#editor",
plugins: "...",
toolbar: "...",
editable_root: false,
editable_class: "editable",
setup: (editor) => {
editor.ui.registry.addAutocompleter("example", {
trigger: "$$", // '@' triggers the autocompleter
fetch: function (pattern) {
// Fetch and return autocomplete suggestions based on the pattern
},
});
},
});
However, in TinyMCE 7.0, the default value for this option has been changed to true. As a result, the editor now shows a focus outline by default when you click inside it.
This change enhances the user experience by providing better visual feedback as well as accessibility improvements, indicating that the editor is active and ready for input.
If you are satisfied with this new default behavior, you don't need to make any changes to your configuration. However, if you have created custom skins or have implemented your own way of displaying focus outlines and prefer not to use the default focus outline, you will need to explicitly set highlight_on_focus to false in your TinyMCE 7 configuration.
tinymce.init({
selector: "textarea",
highlight_on_focus: false,
// Other configurations...
});
RGB colors are HEX colors
In TinyMCE 6, colors in the content HTML were set using RGB values by default and were retained as such unless specified otherwise by the force_hex_color option:
<p>
Hello <span style="color: rgb(255, 0, 0);">World!</span>
</p>;
However, since hex values are more commonly used in web development, TinyMCE 7.0 has reverted to using hex values for RGB values in absolute value like rgb(255, 255, 255) in content HTML and the force_hex_color has been removed.
Now, the same red color from the example above is represented as:
<p>
Hello <span style="color: #ff0000;">World!</span>
</p>;
Media plugin’s resolver returns a Promise
In TinyMCE 7, the media_url_resolver option has been updated to use Promises instead of callbacks. This change aims to simplify the code and avoid unexpected behavior that could occur with the callback implementation.
If you upgrade to TinyMCE 7 without updating your media resolver to use Promises, you may encounter an error message “Media embed handler threw unknown error” as well as unexpected behavior of the embedding:
Old Implementation (TinyMCE 6 and Earlier)
tinymce.init({
selector: "textarea", // change this value according to your HTML
plugins: "media",
toolbar: "media",
media_url_resolver: (data, resolve, reject) => {
if (data.url.indexOf("YOUR_SPECIAL_VIDEO_URL") !== -1) {
const embedHtml = `<iframe src="${data.url}" width="400" height="400" ></iframe>`;
resolve({ html: embedHtml });
} else {
resolve({ html: "" });
}
},
});
New Implementation (TinyMCE 7):
tinymce.init({
selector: "textarea", // change this value according to your HTML
plugins: "media",
toolbar: "media",
media_url_resolver: (data) => {
return new Promise((resolve) => {
if (data.url.indexOf("YOUR_SPECIAL_VIDEO_URL") !== -1) {
const embedHtml = `<iframe src="${data.url}" width="400" height="400" ></iframe>`;
resolve({ html: embedHtml });
} else {
resolve({ html: "" });
}
});
},
});
In this updated example, the media_url_resolver returns a Promise, which resolves with the HTML to embed the media if the URL matches certain criteria, and by doing so, it should fix your media embeddings.
Templates is a Premium Plugin
In TinyMCE 7.0, the open-source free Template plugin and its associated configuration options have been removed. Users previously relying on this plugin are advised to switch to the premium Templates plugin.
Here’s an example of what the templates plugin would have looked like before version 7:
tinymce.init({
selector: "textarea", // Change this value according to your HTML
plugins: "template",
toolbar: "template",
templates: [
{
title: "Some title 1",
description: "Some desc 1",
content: "<p><h2> Template 1 </h2></p>",
},
{
title: "Some title 2",
description: "Some desc 2",
content: "<p><h1> Template 2 </h2></p>",
},
],
});
In TinyMCE 7, the premium plugin, formerly known as the Advanced Template plugin, has been renamed to Templates. You should continue to use advtemplate in your configuration. After updating, this is what the templates plugin in premium would look like:
tinymce.init({
selector: "textarea",
plugins: "advtemplate", // Change this to advtemplate
toolbar: "inserttemplate", // Change this value inserttemplate, you can also configure to add the option to addtemplate in the toolbar
advtemplate_templates: [
{
title: "Some title 1",
description: "Some desc 1",
content: "<p><h2> Template 1 </h2></p>",
},
{
title: "Some title 2",
description: "Some desc 2",
content: "<p><h1> Template 2 </h2></p>",
},
],
});
Tables Content HTML is cleaner
In TinyMCE 7.0, the way cell and row heights are applied to tables has been changed to produce cleaner HTML and simplify height management.
In earlier versions of TinyMCE, resizing table rows added multiple height styles to various elements such as the table (<table>), table row (<tr>), and table cell (<td>). This led to unnecessarily verbose and cluttered HTML content output, with height attributes redundantly applied to multiple elements. In this version, two changes were made: removing Height Input from Cell Properties as well as streamlining the height within tables.
Removed Height Input Field
The height input field has been removed from the "Cell Properties" dialog. Now, the only way to update row heights is through the "Row Properties" dialog. This change simplifies how row heights are managed, removing unnecessary duplicates.
Streamlined Applying Height
When resizing tables using resize handles or the "Row Properties" dialog, TinyMCE 7.0 now strips existing height styles from td and th elements where applicable as can be seen in the screenshots below. Height styles are only applied to the table and tr elements, resulting in cleaner HTML.
The image on the left shows a 2x2 Table with custom Row and Column Heights, while the image on the right shows the same 2x2 table where you can see the td elements don’t have the repetitive height attribute.
There is no fallback to revert to the old behavior, so users must adapt to this new, more efficient way of handling table row heights in TinyMCE 7.0.
All Custom Notifications have a Close Button
In previous versions of TinyMCE, notifications could be displayed without a close button (X). However, this created an accessibility issue because notifications without a close button could not be closed using keyboard navigation.
To address this, TinyMCE 7.0 has removed the closeButton property from the notification API. Now, all notifications will always display a visible close button, ensuring they can be closed using the Tab key.
Before TinyMCE 7.0
In earlier versions, you could create a notification without a close button like this:
tinymce.activeEditor.notificationManager.open({
text: "TinyMCE loaded Successfully!",
type: "success",
closeButton: false, // Removes close button in the notification
});
After TinyMCE 7.0
As of TinyMCE 7.0, the closeButton property is no longer available. All notifications will include a close button by default, making them accessible and allowing users to close notifications using keyboard navigation.
Now, you would simply create a notification like this:
tinymce.activeEditor.notificationManager.open({
text: "TinyMCE loaded Successfully!",
type: "success", // No need to specify closeButton, as it will be shown by default
});
This change improves the accessibility of TinyMCE, making it easier for all users to interact with notifications.
Security Updates
iFrames are sandboxed by default
TinyMCE version 6.8.1 introduced the sandbox_iframes option. By default, this option was set to false, meaning that when you inserted an iframe into the editor, it wasn't sandboxed. A sandboxed iframe is one that has restrictions on what it can do, like running scripts or accessing other resources on the same domain.
Starting from TinyMCE 7.0, the default value for sandbox_iframes has been changed to true. All iframes inserted into the editor will now be sandboxed by default. A sandboxed iframe looks like this:
<iframe src="example.com" sandbox=""></iframe>;
NOTE: This change is for security reasons, but it might cause some existing content to break or behave differently because sandboxing restricts what the iframe can do.
How to Manage This?
- If you need certain iframes to not be sandboxed, you can use the sandbox_iframes_exclusions option to specify which source domains should be excluded from sandboxing.
- If you want to disable sandboxing for all iframes, you can set sandbox_iframes to false in your configuration:
tinymce.init({ selector: "textarea", sandbox_iframes: false });
Embeds are converted to familiar HTML content by default
TinyMCE v6.8.1 also introduced the convert_unsafe_embeds option. By default, this was set to false, meaning that object and embed elements were inserted as they were.
Starting from TinyMCE 7.0, the default value for convert_unsafe_embeds has been changed to true. This means that when you insert object and embed tags, they will automatically be converted to safer html elements like <img>, <video>, or <audio>.
For example, let's say you want to embed a .png image, specifically tiny_husky.png. Previously, you might create an object element with the source and type like this:
<object
data="tiny_husky.png"
type="image/png"
width="400"
height="300"
></object>;
TinyMCE's content HTML would retain the <object> attribute as shown below:
However, since this is an image, you would ideally want the HTML to reflect that with an <img> tag. In versions before TinyMCE 7, you would need to set convert_unsafe_embeds to true in your configuration to achieve this conversion.
In TinyMCE 7, this is the default behavior, so the HTML content would automatically render the Tiny Husky image as an <img> tag, as shown below:
Final points for upgrading to TinyMCE 7
You can have a test drive of the TinyMCE community edition for Version 7 to see how the updates and improvements work within your project. Refer to the Migration guide for more information about upgrading to TinyMCE 7. It contains extensive lists of option and API deprecations and adjustments and security updates.
- If you have any questions, concerns or feedback, reach out to us on the TinyMCE Github
- If you’re aiming to upgrade to use Premium plugins with TinyMCE 7, sign up for a FREE API key, and then try out some of the TinyMCE Premium plugins in your project.
Want to talk to us?
When you’re ready, our sales team is here to chat about available plans to suit your app’s needs (all of which come with professional support to help with your upgrade).
You can also find us on these channels: