Oracle APEX version 20.2 comes bundled with a new Rich Text Editor widget. It is based on the CKEditor5 JavaScript library, whereas before it was leveraging CKEditor4. This is a bigger deal than it seems, as version 5 is a complete rewrite of the library and is API-incompatible with previous versions.
Part 1: An Overview
What does this move mean for existing apps?
If you’ve upgraded your APEX instance to 20.2, you should not see any differences right away in your existing apps, as CKEditor4 is still bundled in APEX 20.2. You will however see a deprecation notice in the new item attribute called Version. You can switch to v5 yourself by changing this attribute. Note that you won’t be able to switch back.

For newly created items, you won’t see this Version attribute at all, as the Rich Text Editor will by default be based on CKEditor5. There is no way to create new items based on CKEditor4 in APEX 20.2.
The APEX team chose to bundle both versions for 1 APEX iteration so no apps would break in case of an automatic upgrade – say on an Oracle-maintained APEX instance on the Oracle Cloud. This way, developers still have some time to manually update the version themselves, check for, and fix any possible issues that may arise from this move.
What happens if I don’t upgrade to CKEditor5?
In APEX 20.2, your Rich Text Editor items will continue to work as before. Come the APEX 21.1 upgrade however, if they haven’t been manually updated, they will be updated automatically.
This means that if you’ve written any custom JavaScript code to alter the widget’s appearance or behavior, you will probably start seeing JavaScript errors pop up, possibly making your page unusable.
It is a good idea to update all instances manually now, to spare yourself a possible headache later.
Why the move to CKEditor5?
CKEditor4 is a wildly popular, stable, and feature-rich WYSIWYG editor. It is however rather outdated – in terms of looks, architecture, and security mechanisms.
The team behind CKEditor decided that in order to keep up with the competition they needed to completely rebuild the editor – using ES6, a new data modal, a new design, a more modern API, better accessibility, a stricter approach to security, and more. Even though they have guaranteed support for CKEditor4 until 2023, the general recommendation for everyone is to start moving to version 5.
You can read more about the differences between v4 and v5 here.
How exactly is the APEX Rich Text Editor item now different?
Some of the differences include, but are not limited to:
1) The look
The new editor features a more modern, flat, simple design. Definitely a welcome enhancement!


2) Interactive Grid Compatible
The editor no longer needs an iFrame element to hold its content – which means it can be moved freely around the DOM – which means it can finally be used as an Interactive Grid column type!
Report View:

Single Row View:

3) Markdown Mode
By default, the Rich Text Editor takes HTML as input and likewise outputs HTML.
CKEditor5 however also comes with a Markdown plug-in. This means we can feed the editor Markdown content, edit it in a WYSIWYG way, and the final item value will magically be Markdown again.

I doubt this feature will be widely used, but it is awesome nonetheless.
Note #1: If you do go the Markdown route, you might have to programmatically fiddle with the toolbar items, as not all of them would produce valid Markdown. For example, the Underline action has no Markdown counterpart and would simply produce <u>u tags</u>.
Note #2: APEX 20.2 comes bundled with CKEditor5 v21.1.0. Since this version was released, a few updates have come out which greatly improve the Markdown experience. I would expect APEX 21.1 to benefit from the new enhancements.
4) A Simplified Toolbar
You will notice the updated page item lacks some of the old attributes. The toolbar can no longer be positioned at the bottom of the editor, and can no longer be collapsed. Moreover, toolbar items will no longer overflow onto a new line but will be hidden away in an overflow menu. This is actually great for mobile users.

5) Gone is View Source
CKEditor5 does not offer all of the same features as CKEditor4. One missing feature that is often brought up is the ability to view and edit the HTML source directly.
The View/Edit Source mode would go against the new security approach of v5, as well as the new data model, which only understands what it’s programmed to understand, and not any arbitrary HTML. For example, if the editor’s input contains some element or attribute that none of the editor’s plug-ins knows how to interpret, it will get ignored. This is a big difference in how v4 and v5 operate.
For more info, see this Twitter thread and this GitHub issue.
If this functionality is absolutely critical in your apps, you can use the free CKEditor4 plug-in offered by FOS.
Part 2: Customizing the Editor via JavaScript
As mentioned previously, the new widget is API-incompatible with the old one. Some differences include:
The Global Object
In CKEditor4, the global library object was called CKEDITOR, it is now ClassicEditor.
Getting a handle on the widget instance
In CKEditor4, you could execute CKEDITOR.instances.P1_ITEM, you can now do the same via apex.item(‘P1_ITEM’).getEditor()
Plug-ins
There are no CKEditor external plug-ins anymore, and it looks like none can be added dynamically either. To see what plug-ins you have access to, run the following in the browser console on a page containing a Rich Text Editor.
ClassicEditor.builtinPlugins.map(plugin => plugin.pluginName)
JavaScript Initialization Code
Configuring the editor via this attribute has also changed. In short, all customization will have to go through options.editorOptions
function(options){ // add your changes to: options.editorOptions return options; }
Of course, to know what changes we can actually set, we must consult the API documentation. Let’s have a look at some examples:
Example #1 Customizing the toolbar
Setting the JavaScript Initialization Code attribute to the following:
function(options){ options.editorOptions.toolbar = [ 'heading', '|', 'bold', 'italic', 'underline', '|', 'todoList', 'insertTable' ]; return options; }
Will result in:

Example #2 Customizing a feature
The default table widget inserts a table, but a rather boring one. We can enable all table controls, doing the following:
function(options){ options.editorOptions.table = { contentToolbar: [ 'tableColumn', 'tableRow', 'mergeTableCells', 'tableProperties', 'tableCellProperties' ] }; return options; }
Which will display an extra toolbar when working with tables:

Example #3 Creating a custom toolbar button
While probably not a very common requirement, the following example shows how to create a simple toolbar button:
function(options){ class CustomButton extends ClassicEditor.libraryClasses.Plugin { init(){ const editor = this.editor; editor.ui.componentFactory.add('customButton', locale => { const view = new ClassicEditor.libraryClasses.ButtonView(locale); view.set({ // an icon can be provided as SVG label: 'Custom Button', withText: true, tooltip: true }); view.on('execute', () => { alert('cutom button clicked!'); }); return view; }); } } options.editorOptions.extraPlugins.push(CustomButton); options.editorOptions.toolbar.push('customButton'); return options; }
Resulting in:

When creating custom elements for CKEditor5, such as a toolbar button, you will need to reference some base utility classes, such as Plugin or ButtonView. Unfortunately, not all of these classes are available in the CKEditor5 build that comes bundled in APEX.
In its natural habitat, CKEditor5 is meant to be built and extended in a static NodeJS context, where all of these helper classes can be easily referenced, used, and included in the final build. In an APEX context, we do not have direct access to all of these classes.
Luckily, 2 classes were exposed under ClassicEditor.libraryClasses, exactly Plugin and ButtonView. These should suffice for building simple buttons. I suspect more utility classes will be exposed in future APEX versions, but realistically, this distribution will never be as versatile as a custom build.
Example #4 Interacting with the widget after initialization
To end on a simpler example, this is how you would dynamically toggle the readOnly mode of the editor:
apex.item('P1_EDITOR').getEditor().isReadOnly = true; // or false
You would run this code after initialization, say in a dynamic action.
Part 3: Conclusion
The move to CKEditor5 might seem a bit disruptive, but it had to happen at some point. I for one am very grateful for the new modern design, and the ability to use the Rich Text Editor as an Interactive Grid column.
Regarding the library itself, simply by looking at its GitHub repository and release log, development is moving extremely fast, with new features and plug-ins being introduced at an impressive rate.
I look forward to exploring this Rich Text Editor further in future blog posts.
How to remove some of the plugins from editor CKEditior5
How to remove “Autoformat” plugin from Editor in JavaScript Initialization Code.
Tried options.editorOptions.removePlugins = ‘Autoformat’; It is not working. is something missing ?
Hey!
removePlugins should be an array, hence:
options.editorOptions.removePlugins = [‘Autoformat’];
Thanks a lot. Good Job Stefan. Good and Detailed info provided by you.
It works for me.
Hy,
in rich text editor to show all property like Source, Button, text field etc …how it possible because of in CK4 version its possible but not in CK5….
Hi Stefan,
If I want to use the WordCount plugin ?
What I should use ?
function(options){
options.editorOptions.addPlugins = [‘WordCount’];
return options;
}
And then I add a new region with this static id container-for-word-count?
(This is not working for me)
Thank you
Hi Eric,
First off, you don’t need to add this plug-in, it’s part of the editor by default. I suppose it’s just not initialized.
Secondly, by just following their tutorial over at https://ckeditor.com/docs/ckeditor5/latest/features/word-count.html, it seems we’re supposed to append that container element *after* the editor has been initialized. We could do this on page load, but this isn’t very elegant. One thing I haven’t mentioned in the blog post, is the optional initialization function we can provide under “options”. This will receive as parameter the editor instance, and it will run *before* things like On Page Load dynamic actions. This is the perfect place to mess with the editor one last time.
The following worked for me:
function(options){
options.executeOnInitialization = function(editor){
const plugin = editor.plugins.get(‘WordCount’);
editor.sourceElement.parentElement.parentElement.appendChild(plugin.wordCountContainer);
};
return options;
}
In here we can insert the element provided by the WordCount plug-in, right below our widget.
The .parentElement.parentElement part isn’t ideal, but it does the job.
We also make use of editor.sourceElement so we don’t hard code the item name.
Hope this works for you.
Hi Stefan.
It worked.
Thank you.
Hi Eric,
Where exactly do you place the code? in the Execute when Page Loads where the editor is?
Do I need the WordCount plug-in? where do I find it?
Sorry for my ignorance. I would like to integrate the word count feature and I don’t know how to do it.
Thanks for any help you can give me!
Javier
Hi Javier,
This code goes in the “Javascript Initialization Code” attribute of the editor item.
You do not need an extra plug-in. It’s already built in.
Hi Stefan,
Very nice article!
How can we add images into the editor?
I don’t see the image button in order to be able to add images.
Thanks
Javier
Hi Javier,
Images are a bit tricker, because.. where would they be stored? Embedded as base64, uploaded separately and just referenced with a link?
The editor does come with an image plugin, but this is only for the front end, it knows nothing about our database backend.
I will try one of these days to get this working, by leveraging a separate table and REST endpoint. But until then I’m afraid it’s not a trivial task.
Regards,
Stefan
HI Stefan,
Thanks for you help!!
Is this the exact code?
function(options){
options.executeOnInitialization = function(editor){
const plugin = editor.plugins.get(‘WordCount’);
editor.sourceElement.parentElement.parentElement.appendChild(plugin.wordCountContainer);
};
return options;
}
Because when I put it in the Javascript Inizialization Code the page don’t load. It stays blank.
About the image, keep us post about what you find.
It would be great to have it Embedded as base64 to be able to send an email with it.’
Thanks again!
I put the code in the
Hi again,
yeah that code looks good to me.
Does it also not work on apex.oracle.com? If you could create a test case there and share the url I could take a look.
Hi Stefan,
No, it does not work on apex.oracle.com
Am not sure but do I need to install a plugin? which one?
If you have a chance and can check it.
WS: INFOPRO
u: demo
p: demo1234
Application: RIch Text Editor
When you load the page it wont load. I have the code in the Javascript initialization property
I also trying to get the enter key to go just one space when press with this code also in the javascript initialization but it wort work in the new editor:
function ( configObject ) {
configObject.enterMode = 2;
configObject.uiColor = “#AADC6E”;
configObject.resize_enabled = false;
return configObject;
}
Do you know if it works like this in the new editor?
Thanks a lot for you help!
Hi Javier,
I took a look. You were using the wrong quotes around the WordCount string. You must have copy pasted the code directly from from that WordPress comment, which autoformatted the quotes. I really need to get a better comment system on here.
Anyway I fixed the code in your app.
As for the 2nd problem:
This code will not work with CKEditor5 anymore. The help text on the JavaScript Initialization Code attribute of the item is outdated. I’m assuming it will be revamped in 21.1 when CKEditor4 is gone completely. Based on a quick Google Search, CKEditor5 does not have an option equivalent to “enterMode”. The Enter key will always create a new paragraph. A workaround is to use Shrift+Enter to insert a as opposed to a paragraph.
Hope this helps.
Hi Stefan,
It helps a lot. Thanks for taking the time!!!
One last thing. How can I eliminate the words count?
Just leave the characters count? is this possible.
Thanks again for your help!!
Sure. You just need the following:
options.editorOptions.wordCount = {
displayWords: false
};
I added it to your app.
For more info and advanced configuration, see:
https://ckeditor.com/docs/ckeditor5/latest/api/module_word-count_wordcount-WordCountConfig.html#member-displayWords
Hi,
Probably not the main scope of what you tell us.
However, what’s about the 32k limit (C/B-LOB support)?
I know a question more in direction to the APEX team…
Hey Andre,
Nothing changed with respect to the 32k limit. It is still the same as for all other page items.
I also haven’t heard of any plans from the APEX team regarding introducing CLOB support, but I hope I’m wrong.
However I am working on a blog post right now showing how to embed images in the editor, as well as working on a plug-in for easier handling of large values. Hope these 2 will help a bit.
Thanks Stefan for all your help!!!
Hi Stefan!
First of all, thank You very much for your helpful work. I appreciate it because I am a fan of APEX but not an expert in JavaScript. I have in my Apex v20.1 instance a useful application with Rich Text Editor (RTE) Ckeditor v4 Page Item. Now I am going to upgrade to Apex 20.2 and the RTE to CKeditor v5.
In Apex 20.2 I created an RTE Page Item and I faced immediately several problems:
1. The toolbar: Based on your article using plug-ins and Example #1, I already assembled my toolbar quite similar to RTE v4.
My code is:
function(options){
options.editorOptions.toolbar =
[
‘heading’,
‘|’, ‘code’, ‘codeBlock’, ‘blockQuote’,
‘|’, ‘bold’, ‘italic’, ‘underline’, ‘strikethrough’, ‘fontfamily’, ‘fontsize’, ‘RemoveFormat’, ‘link’,
‘|’, ‘numberedList’, ‘bulletedList’, ‘outdent’, ‘indent’, ‘Alignment’, ‘-‘,
‘|’, ‘InsertTable’, ‘fontColor’, ‘fontBackgroundColor’,
‘|’, ‘imageUpload’,
‘|’, ‘PageBreak’, ‘HorizontalLine’, ‘todoList’, ‘undo’, ‘redo’
];
return options;
}
Here I would appreciate your help because:
a. I really missing widgets such as Preview, Print (only the RTE Item, not the page), subscript, superscript
b. I could not figure out how to get a multiline toolbar. (I tried to use ‘-‘ but it didn’t work.
2. Enable all table controls: I left the default toolbar, inserted your code into JavaScript Initialization Code of the RTE Page IItem and nothing changed.
3. Is it possible to have my own customized toolbar and to enable all table control at the same time?
Looking for your answer.
Best regards: Jozsef
Hi Jozsef,
There are no Preview or Print widgets in CKE5. I also don’t think any will be provided, as it seems they’re trying to push a commercial addon: https://ckeditor.com/export-to-pdf-word/
A custom plugin for preview/print could be implemented, similar to how it used to work in CKE4 (https://github.com/ckeditor/ckeditor4/blob/master/plugins/preview/plugin.js). I might have a look at this after the Holidays, but I can’t promise anything.
Subscript & Superscript are unfortunately 2 of the plugins missing from the APEX distribution of CKE5, but they should be added in APEX 21.1. I could try to provide a self made implementation, but again, I can’t promise anything.
A traditional multiline toolbar does not exist in the version of CKE5 APEX 20.2 comes with, but it will exist in the next APEX version. Then you will be able to use the “-” toolbar entry. Until then however, you could disable the overflow menu. This should achieve a “similar” result. See the code at the end of this comment.
The table controls work for me. Of course you should be able to combine them with the custom toolbar. Please try again with the following code:
https://gist.github.com/stefandobre/bc14e6661742e263ad49a94079e08cd0
Dear Stefan!
Thank you for your quick, kind, and detailed answer.
First of all, let me wish you a Happy New Year!
Then I have good news: I figured out how to combine my custom toolbar with table control. It works fine, thank you very much.
After testing the Apex 20.2 and the CKEditor5 on a virtual machine finally I upgraded my Apex on my host. It is great.
The only thing that I found is the font_color of the “Code Block”:
I like the dark mode (using Theme Vita – Dark) because it is really fine for my eyes. However in Dark Mode, the text with the style “Code Block” is invisible because the font_color is too dark.
As opposed to this behavior text with the style “Code” is fine, because in dark mode the font_color switches to light.
Could you offer any solution to how I could change the “Code Block” font_color to a bright one?
Just I’ve tried your code with shouldNotGroupWhenFull: true. It is working. Thanks a lot.
Hi Jozsef,
Glad to be of assistance. Happy new year to you too!
Thanks for catching that dark mode issue. I’ll report it and it should be fixed in 21.1.
Until then, try this CSS. Either inline on the page, or globally somewhere.
body.apex-theme-vita-dark .ck-content pre{
color: #fefefe;
}
Let me know if it works,
Stefan
Hi Stefan!
It works. Heureka. Looks nice.
Thanks a lot.
Dear Stefan!
I know this is not related to CKEdior but It is related to my knowledge base application, based on CKEditor. :-). I surfed the web for hours and couldn’t find the right solution.
Could you help me to find how to change the font and background color of the selected node in a region which has Type: Tree
Thanks in forward.
Dear Stefan!
I know this is not related to CKEdior but It is related to my knowledge base application, based on CKEditor. :-). I surfed the web for hours and couldn’t find the right solution.
Could you help me to find how to change the font and background color of the selected node in a region which has Type: Tree and Theme Style: Vita – Dark.
Apex version: 20.2
Thanks in forward.
Appreciate for Your experience sharing!
Would You clarify is apex.item(‘P1_ITEM’).getEditor() documented APEX API function? I didn’t find out it here https://docs.oracle.com/en/database/oracle/application-express/20.2/aexjs/apex.item.html
Hi Sasha,
from what I can tell only the interface/common item functions are documented, not specific functions to individual items.
Perhaps this should change though.
Using .getEditor() should however be safe.
Regards,
Stefan
Hi, Stefan.
I’m in stuck to figure out a object model of CKEDITOR https://ckeditor.com/docs/ckeditor5/latest/api/module_editor-classic_classiceditor-ClassicEditor.html
Would You clarify what JS-code corresponds to that init APEX code:
function(options){
options.editorOptions.toolbar = [
‘heading’, ‘|’,
‘bold’, ‘italic’, ‘underline’, ‘|’,
‘todoList’, ‘insertTable’
];
return options;
}
I mean direct JS-access to ClassicEditor object:
var l_Editor = apex.item(‘ITEM_NAME’).getEditor();
l_Editor.???.options.editorOptions.toolbar = [
‘heading’, ‘|’,
‘bold’, ‘italic’, ‘underline’, ‘|’,
‘todoList’, ‘insertTable’
];
Thank You.
“options” and “editorOptions” are constructs of the APEX Javascript Initialization side of things. They are arbitrary names chosen to initialize this component and you won’t find them in the CKEditor5 documentation.
It also seems that you’re trying to set the toolbar after initialization- I don’t *believe* this is possible. I could be wrong though.
I guess, You are right about “mapping”… ((( While we are reading APEX IDE help:
JavaScript Initialization Code
Enter a JavaScript function to override standard CKEditor initialization. The function must return a javascript object containing the configuration for CKEditor. See CKEditor documentation for a list of available attributes.
Where i should read about available attributes if that names are arbitrary?! One more harm of APEX from developer team. Hate it!
I explained it poorly.
“options” was arbitrarily chosen in that, being a function parameter name, you can choose whatever you want.
function(whatever){
whatever.xyz = 123;
return whatever;
}
“editorOptions” had to be an arbitrary name, because it is the container where all CKEditor5 options will go. It might as well have been called “options” again, but “options.options” isn’t too pretty.
Now in this editorOptions object, you can set any option/setting/config from the CKEditor5 docs.
You can find the list here:
https://ckeditor.com/docs/ckeditor5/latest/api/module_core_editor_editorconfig-EditorConfig.html
So you can do:
function(options){
options.editorOptions.alignment = …
options.editorOptions.autosave = …
options.editorOptions.codeBlock = …
options.editorOptions.fontColor = …
options.editorOptions.toolbar = …
// etc
return options;
}
Appreciate for Yuor answers. I understand, that “options” is a formal parameter of function cal. So it has arbitrary name being a root of CKEDITOR object model. It;s ok. But my understanding was that after “options” should follow real named classes and properties to access a real object tree… A mean options.RealClass.RealProperty not only @ckeditor/ckeditor5-core/src/editor/editorconfig
It all sounds very strange )))
Hi Stefan,
we’ve just upgraded all our applications to APEX 20.2 and imeditately noticed that the CKEDITOR5 doesn’t have the feature of CKEDITOR4 to allow the user to change the size of the editor (like this text area where I enter this text has at the right bottom corner). Do you know if this can be added somehow?
Also there seems to be a bug when editor is set as readonly: the Maximum Height value from “Settings” is ignored and the text is displayed in full. Do you also know a workaround for this?
Thank you,
Viorel
Hi All,
Looking for little bit of help. I have an Apex 20 page with a variety of page items some of which are Rich Text Fields (RTFs). I really like the look of the page based on my theme settings, however I want the Rich Text Fields to always have a background color of white – currently they only turn white on hover. My question is therefore is there a way to override the Universal Theme such that RTFs are always displayed with a white background?
Mike
Thanks for the great introduction to the editor Stefan,
I’m wondering whether I can color either the background or the toolbar based on the length of the text in the field matching or not matching the length of another field… I’m trying to set up an A/B page for about half a dozen fields, all rich text.
I’ve tried reading through the documentation on the editor API, but I can’t find how to set the colors with the new version 5 editor.
Any help sincerely appreciated.
Hello ! Please help me
Answer my question please.
How can I add my custom font styles to rich text editor (not ‘text-tiny’, ‘text-small’ and either and not numerical font sizes)
And how delete default browser formatting from rich text editor ?
I want to add them to my custom css file in Shared Components –> User Interface Attributes. For example now :
.text-tiny {
font-family: ‘IBMPlexSans-Regular’, sans-serif;
font-style: normal;
font-weight: normal;
font-size: 75%;
line-height: 100%;
color: #000000;
}
I want :
.my_custom_style {
font-family: ‘IBMPlexSans-Regular’, sans-serif;
font-style: normal;
font-weight: normal;
font-size: 75%;
line-height: 100%;
color: #000000;
}
I have apex 20.2 and I can’t do this :
options.editorOptions.stylesSet.add(‘my_custom_style’, [
{ name: ‘My Custom Block’, element: ‘h3’, styles: { color: ‘blue’} },
{ name: ‘My Custom Inline’, element: ‘span’, attributes: {‘class’: ‘mine’} }
]);
options.editorOptions.font_defaultLabel = ‘IBMPlexSans-Regular, sans-serif’;
options.editorOptions.fontSize_defaultLabel = ’18px’;
Hi ! Help me please
I have apex 20.2
In my rich text editor item JavaScript Initialization Code I have function with fontSize option :
options: [
‘tiny’,
‘small’,
‘big’,
‘huge’
]
It is default FontSizes, but I want add some custom FontSizes (not numerical).
How can I do this ?
Hi Stefan,
Thank you for sharing this blog post.
We have stored html with CK Editor 4. Once we upgrade our page item to CK Editor 5 and fetch the existing html, it appears that the editor converts our html stored in CK Editor 4. When leaving the page unsaved, alert Warn on Unsaved Changes prompts, although the user did not change any data.
Looking in the database CLOB column with CK Editor 4 html, CK Editor 5 has changed
Check by "Show XML File".
to
Check by “Show XML File”.
Should we migrate the html stored with CK Editor 4? If yes, how to migrate?
Looking forward to your response.
Johan