Splitting the CKEditor/TinyMCE Difference in XPages on Domino 14.5
Wed Feb 04 14:29:09 EST 2026
- Oct 19 2018 - AbstractCompiledPage, Missing Plugins, and MANIFEST.MF in FP10 and V10
- Jan 07 2020 - Domino 11's Java Switch Fallout
- Jan 29 2021 - fontconfig, Java, and Domino 11
- Nov 17 2022 - Notes/Domino 12.0.2 Fallout
- Dec 15 2023 - Notes/Domino 14 Fallout
- Sep 12 2024 - PSA: ndext JARs on Designer 14 FP1 and FP2
- Dec 16 2024 - PSA: XPages Breaking Changes in 14.0 FP3
- Jun 17 2025 - Notes/Domino 14.5 Fallout
- Feb 04 2026 - Quick Tip: Stable ndext Classpaths In Designer 14+
- Feb 04 2026 - Splitting the CKEditor/TinyMCE Difference in XPages on Domino 14.5
At the top of the update notes for Designer 14.5 is this bit:
My guess is the reason for this change is that CKEditor 4 hit end-of-life in June 2023 and CKEditor 5 dropped the option to license as MPL, meaning the only way to use it in Domino would be to pay for a presumably-onerous enterprise license†. Fair enough.
However, you may notice that the documentation for this change is a little... thin. Specifically, the screenshot above is the documentation. Considering that customizing CKEditor has been a popular and long-documented feature of XPages, it's possible you're sitting on some code that used to customize CKEditor to your needs but does not work in 14.5. Since CKEditor no longer ships with Domino, there's no opt-in switch, so we have to deal with it.
Well, I don't have all the answers for how to do this properly, but I did recently have a need to at least start digging into it, so I figured I'd share a preliminary tack to start making cross-version-compatible code.
Customizing TinyMCE
Like CKEditor, TinyMCE has mechanisms to customize its appearance in both simple and programmatic ways. The way TinyMCE is initialized is that it's passed a configuration object during creation, like in this example from their docs:
1 2 3 4 5 | tinymce.init({
selector: 'textarea',
skin: 'oxide-dark',
content_css: 'dark'
});
|
As it happens, the Dojo plugin XPages uses to instantiate TinyMCE (still called "ibm/xsp/widget/layout/CKEditorWrapper" for some reason) takes all of your HTML attributes and passes them to this object, vaguely similar to the way CKEditor did it. That means you can get the above behavior like so (we don't need the selector, since XPages does that for us):
1 2 3 4 5 6 | <xp:inputRichText> <xp:this.attrs> <xp:attr name="skin" value="oxide-dark"/> <xp:attr name="content_css" value="dark"/> </xp:this.attrs> </xp:inputRichText> |
I say "vaguely similar" because, while CKEditor will interpret applicable attributes (like toolbar) as JavaScript by running them through eval(...), the same is not the case for the TinyMCE code. So, for example, this snippet, trying to replicate the next example from the TinyMCE page, will not work:
1 2 3 4 5 6 | <xp:inputRichText> <xp:this.attrs> <xp:attr name="skin" value="(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'oxide-dark' : 'oxide')"/> <xp:attr name="content_css" value="(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'default')"/> </xp:this.attrs> </xp:inputRichText> |
That will just cause the page to try to load the URL "/yourapp.nsf/(window.matchMedia('(prefers-color-scheme:%20dark)').matches%20? 'dark' : 'default')=", which will, uh, not work. I assume you can deal with this via TinyMCE plugins, but I haven't looked into those yet.
When The Twain Meet
If you're migrating from older versions to 14.5+, your task is to modify all of your code that customizes CKEditor. The most common "good" case for this is that your customizations break in innocuous ways. However, it's also possible that your customizations will result in a broken editor, particularly around toolbars. For example, the afore-linked documentation from 8.5.2 includes the GUI version of this:
1 2 3 4 5 | <xp:inputRichText> <xp:this.dojoAttributes> <xp:dojoAttribute name="toolbarType" value="Large"/> </xp:this.dojoAttributes> </xp:inputRichText> |
(for the record, xp:this.dojoAttributes is effectively interchangeable with xp:this.attrs for this use)
This causes CKEditor to display its "large"-type suite of toolbar icons, as opposed to "medium" or "slim". However, this causes TinyMCE to display no toolbar icons at all. The reason for this is that CKEditorWrapper.js maps "toolbarType" to just "toolbar" and, while CKEditor treated "toolbar" as either a shorthand name or as JavaScript, TinyMCE treats the same attribute as a space-delimited list of action names, and "Large" is not one.
This creates a bit of a pickle! You'll definitely need to open up your app's code to change this. If you're doing a one-way migration, you can change this to be a list of the buttons you'd like, but things will get messier if you want to have multiple versions of Domino deployed, or you want to prep your apps for the migration before making the switch.
There are a lot of ways one could go about doing this and I'm not sure I love the provisional mechanism I'm going to share here, but this is at least one way to do it. Since the one way we can know that we're using a different editor is by the build version of Domino, we can make a switch and only load attributes based on the running server version:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <xp:view xmlns:xp="http://www.ibm.com/xsp/core"> <xp:this.dataContexts> <xp:dataContext var="isTinyMce" value="${javascript:parseInt(session.evaluate('@Version')[0], 10) >= 495}"/> </xp:this.dataContexts> <xp:inputRichText> <xp:this.attrs> <!-- CKEditor attributes --> <xp:attr name="toolbar" value="Slim" loaded="${not isTinyMce}"/> <!-- TinyMCE attributes --> <xp:attr name="toolbar" value="undo redo styles" loaded="${isTinyMce}"/> </xp:this.attrs> </xp:inputRichText> </xp:view> |
If you have a lot of CKEditor customizations, your best bet will be to use a customConfig script to consolidate them if you don't already. TinyMCE will ignore that, so at least it won't do any harm.
Like I said, I don't love this approach, since it'd be a real pain if you have a lot of rich-text fields in your apps, but at least it's a starting point. It could be more scalable (if very fiddly) to write a renderer to replace RichTextRenderer that checks the server version and then does its own heuristics to change the output values for the HTML. It might also be possible to dredge up the old CKEditor files from older Domino and write your own Dojo plugin to re-wrap them to keep things chugging for a bit, but that'd be only a temporary fix.
Anyway, it's a messy situation and I didn't see a lot of documentation for it, so I figured I'd jot this down. Good luck!
† After posting this, I noticed that TinyMCE went through a similar permissive-or-GPL switch in the move from 6.8 to 7.0. That explains why Domino ships with version 6.8.4 and makes me suspect that we'll face another similar move before long.