Blazor Rich Text Editor using CKEditor

I'll start with a disclaimer. I just started working with Blazor and I'm still getting my bearings. I needed to add a rich text editor to a form I was working on, and found the existing options lacking. Currently, all I could find were Blazored Text Editor and Syncfusion's Blazor Rich Text Editor. The Blazored Text Editor uses Quill JS, which I don't really care for. Syncfusion's version seems fine, but it's license-encumbered. My preferred rich text editor is CKEditor 5, so I set about to create a component using that, which meant coming to terms with JS interop.

Blazor is incredibly simple to work with... until it isn't. Integrating random JS libraries is a tall order until you understand how JS interop works, and the docs are pretty minimal here. However, once I got my head around it, it ended up being surprisingly easy. That said, this is literally my first go at something like this, so I would very much welcome any comments or suggestions for improvement. With that out of the way, let's dig in.


@inherits InputTextArea
@inject IJSRuntime JSRuntime

<textarea @attributes="AdditionalAttributes"  

@code { 
    string _id;
    public string Id
        get => _id ?? $"CKEditor_{uid}";
        set => _id = value;

    readonly string uid = Guid.NewGuid().ToString().ToLower().Replace("-", "");

    protected override async Task OnAfterRenderAsync(bool firstRender)
        if (firstRender)
            await JSRuntime.InvokeVoidAsync("CKEditorInterop.init", Id, DotNetObjectReference.Create(this));

        await base.OnAfterRenderAsync(firstRender);

    public Task EditorDataChanged(string data)
        CurrentValue = data;
        return Task.CompletedTask;

    protected override void Dispose(bool disposing)
        JSRuntime.InvokeVoidAsync("CKEditorInterop.destroy", Id);


window.CKEditorInterop = (() => {  
    const editors = { };

    return {
        init(id, dotNetReference) {
                .then(editor => {
                    editors[id] = editor;
                    editor.model.document.on('change:data', () => {
                        let data = editor.getData();

                        const el = document.createElement('div');
                        el.innerHTML = data;
                        if (el.innerText.trim() == '')
                            data = null;

                        dotNetReference.invokeMethodAsync('EditorDataChanged', data);
                .catch(error => console.error(error));
        destroy(id) {
                .then(() => delete editors[id])
                .catch(error => console.log(error));

With those two files in place, add the following to your _Host.cshtml:

    <script src=""></script>
    <script src="~/js/CKEditorInterop.js"></script>
    <script src="_framework/blazor.server.js"></script>
comments powered by Disqus