WYSIWYG editing Image gallery upload Content templates
Migration guide

Migrate from TinyMCE or CKEditor to RichTextBox

Most teams finish the migration in under a day. Your existing HTML content keeps working, toolbar strings map mostly 1→1, and the features you’re paying a premium tier for today ship in our base license. Below: side-by-side config mapping, feature-equivalence tables, and a step-by-step checklist.

~1 day typical migration time for a single-app deployment
$0 / year perpetual license after the one-time purchase
0 features lost full v2.0 collab + AI stack is base-license

Why teams move

  • Perpetual license. No subscription, no per-load metering, no AI usage-credit invoices.
  • One SKU for everything. Track Changes, Comments, Revision History, Mentions, AI — all in the base license. TinyMCE premium billing covers these separately.
  • Self-host at the entry tier. TinyMCE gates self-hosting to Enterprise; we self-host from day one.
  • Native ASP.NET Core Tag Helper<richtextbox> as a first-class Razor citizen, not a JS wrapper.

Step-by-step checklist

  1. Install the NuGet: dotnet add package RichTextBox.AspNetCore --version 1.0.0-preview.12
  2. Register in Program.cs: builder.Services.AddRichTextBox(); · app.MapRichTextBoxUploads();
  3. Drop your RichTextBox.lic file next to Program.cs (contact sales for a trial key — the demo resolver runs key-less).
  4. Replace <textarea id="..."> + TinyMCE init script with a single <richtextbox asp-for="Body" toolbar="full" /> tag.
  5. Map your TinyMCE plugins: and toolbar: config to our toolbar attribute — see the cheat sheet below.
  6. Wire an AI resolver if you use TinyMCE AI: builder.Services.AddRichTextBoxOpenAiResolver(opts => opts.ApiKey = ...). No proxy server needed.
  7. Replace any custom content filters with config.filterhtml / filterbeforepaste callbacks.
  8. Existing HTML content keeps working; submit your forms as normal, the posted value is the editor's HTML.

Config cheat sheet

Basic setup

TinyMCE

tinymce.init({
  selector: '#editor',
  plugins: 'lists link image table code',
  toolbar: 'bold italic | bullist numlist | link image',
  height: 500
});

RichTextBox Tag Helper

<richtextbox
    asp-for="Body"
    toolbar="custom"
    height="500px" />

@* Define "custom" toolbar once at site level *@
<script>
RTE_DefaultConfig.toolbar_custom =
  "{bold,italic}|{insertunorderedlist,insertorderedlist}|{insertlink,insertimage}";
</script>

AI (TinyMCE AI → RichTextBox AI Toolkit)

TinyMCE AI (uses a proxy)

tinymce.init({
  plugins: 'ai',
  toolbar: 'ai',
  ai_request: (request, respondWith) => {
    respondWith.stream((streamMessage) =>
      fetch('/my-proxy-to-openai', {
        method: 'POST',
        body: JSON.stringify({ ... })
      }).then(/* ... */)
    );
  }
});

RichTextBox (one-line DI)

builder.Services.AddRichTextBox();
builder.Services.AddRichTextBoxOpenAiResolver(opts =>
{
    opts.ApiKey = builder.Configuration["OpenAI:ApiKey"];
    opts.Model  = "gpt-4o-mini";
});

@* Page *@
<richtextbox asp-for="Body"
             enable-ai-toolkit="true" />

Track Changes, Comments, Revision History

Three separate TinyMCE premium add-ons → three base-license attributes.

TinyMCE (premium)

tinymce.init({
  plugins: 'tinycomments a11ychecker '
         + 'revisionhistory',
  tinycomments_mode: 'embedded',
  tinycomments_author: 'current user',
  // Track Changes requires separate license
});

RichTextBox

<richtextbox asp-for="Body"
    enable-tracked-changes="true"
    enable-comments="true"
    enable-revision-history="true"
    current-user-id="@User.Id"
    current-user-name="@User.Name" />

Plugin equivalence

TinyMCE pluginRichTextBox equivalent
linkbuilt-in (insertlink toolbar item)
image, imagetoolsbuilt-in (insertimage, imageeditor)
mediabuilt-in (insertvideo, insertyoutube)
tablebuilt-in (inserttable + table control toolbars)
lists, advlistbuilt-in (insertorderedlist, insertunorderedlist, insertchecklist)
codesamplebuilt-in (insertcode, syntaxhighlighter)
emoticonsbuilt-in (insertemoji)
templatebuilt-in (inserttemplate)
mentions (premium)enable-mentions="true" (base license)
tinycomments (premium)enable-comments="true" (base license)
tinymcespellchecker (premium)native browser spellcheck + spellcheck toolbar toggle
revisionhistory (premium)enable-revision-history="true" (base license)
Track Changes (premium)enable-tracked-changes="true" (base license)
ai (premium + credits)enable-ai-toolkit="true" + provider resolver
exportword (premium)built-in DOCX endpoint + editor.aiToolkit.exportDocx()
exportpdf (premium)html2pdf toolbar item (client-side)
wordcountnative via editor.getText().length or host wrapper
autosaverevision history auto-snapshots + form-level autosave

Behavior differences to know about

HTML output. TinyMCE output uses lower-case element / attribute names and non-breaking spaces in empty paragraphs. Our output is case-normalized but otherwise equivalent; strict XHTML-validating backends should still accept it. Customize via config.filterhtml.
Events. TinyMCE's change event → our editor.attachEvent("change", fn). init / setup → our new RichTextEditor(selector, config) is synchronous; bind events right after construction.
Content CSS. TinyMCE content_css: "..." → our config.contentCssUrl or inline config.contentCssText.

What you gain

  • Dictation (mic button, Web Speech API) — TinyMCE doesn’t ship this.
  • Slash commands (Notion-style / inline picker) — more mature than TinyMCE's autocompleter API.
  • Shared review ledger — AI suggestions + human track-changes + comments in one drawer. TinyMCE keeps these siloed.
  • Blazor Server support, ASP.NET Web Forms, Classic ASP, PHP bindings.

Why teams move

  • Perpetual license vs CKEditor’s subscription + add-on SKUs (Track Changes, Comments, Revision History, AI, Real-time Collab are each separate paid modules).
  • Simpler integration — one NuGet, one Tag Helper, done. CKEditor 5 requires webpack / Vite config and custom-build headaches to mix features.
  • Better .NET story — idiomatic ASP.NET Core, MVC, Web Forms, Blazor, and Classic ASP. CKEditor is React/Vue/Angular only.
  • Self-host on entry tier. CKEditor’s enterprise features require custom contract pricing.

Step-by-step checklist

  1. Install the NuGet: dotnet add package RichTextBox.AspNetCore --version 1.0.0-preview.12
  2. Register in Program.cs: builder.Services.AddRichTextBox(); · app.MapRichTextBoxUploads();
  3. Drop RichTextBox.lic next to Program.cs.
  4. Remove your CKEditor custom build / webpack config — we don’t need one.
  5. Replace ClassicEditor.create(...) init code with a single <richtextbox> tag.
  6. Map your CKEditor toolbar: [ ... ] items to our toolbar attribute (see cheat sheet).
  7. Collaboration: replace CKEditor Cloud Services + Yjs plugins with our enable-collab="true" and load Yjs from npm or a CDN.
  8. Paste existing CKEditor content directly — HTML output is compatible.

Config cheat sheet

Basic setup

CKEditor 5 (custom build)

import ClassicEditor from '@ckeditor/ckeditor5-build-classic';

ClassicEditor.create(document.querySelector('#editor'), {
  toolbar: {
    items: ['bold', 'italic', '|',
            'bulletedList', 'numberedList', '|',
            'link', 'imageUpload']
  }
});

RichTextBox Tag Helper

<richtextbox
    asp-for="Body"
    toolbar="custom" />

<script>
RTE_DefaultConfig.toolbar_custom =
  "{bold,italic}|{insertunorderedlist,insertorderedlist}|{insertlink,insertimage}";
</script>

Real-time collaboration

CKEditor Cloud Services (subscription)

ClassicEditor.create(el, {
  cloudServices: {
    tokenUrl: '/api/ckeditor-token',
    webSocketUrl: 'wss://cs.cke-cs.com/...'
  },
  collaboration: {
    channelId: documentId
  }
});

RichTextBox + Yjs (self-host)

<richtextbox asp-for="Body"
    enable-collab="true"
    current-user-id="@User.Id"
    current-user-name="@User.Name" />

<script type="module">
import * as Y from "https://esm.sh/yjs";
import { WebsocketProvider } from "https://esm.sh/y-websocket";
const ydoc = new Y.Doc();
const p = new WebsocketProvider(
    "wss://your-server/yjs", docId, ydoc);
editor.collab.attach({
    doc: ydoc, provider: p,
    textSync: true  // concurrent typing
});
</script>

AI Assistant → AI Toolkit

CKEditor AI (subscription + usage credits)

ClassicEditor.create(el, {
  ai: {
    openAI: {
      requestHeaders: {
        Authorization: 'Bearer ...'
      },
      apiUrl: 'https://api.openai.com/v1/...'
    }
  }
});

RichTextBox (one-line DI)

builder.Services.AddRichTextBox();
builder.Services.AddRichTextBoxOpenAiResolver(opts =>
{
    opts.ApiKey = builder.Configuration["OpenAI:ApiKey"];
    opts.Model  = "gpt-4o-mini";
});

Plugin equivalence

CKEditor 5 plugin / featureRichTextBox equivalent
Bold, Italic, Underlinebuilt-in (bold, italic, underline)
Link / LinkImagebuilt-in (insertlink)
Image / ImageUpload / ImageResizebuilt-in (insertimage, imageeditor)
Table / TablePropertiesbuilt-in (inserttable + table control toolbars)
List / TodoListbuilt-in (insertorderedlist, insertunorderedlist, insertchecklist)
CodeBlockbuilt-in (insertcode)
Mention (premium)enable-mentions="true" (base license)
Comments (premium)enable-comments="true" (base license)
TrackChanges (premium)enable-tracked-changes="true" (base license)
RevisionHistory (premium)enable-revision-history="true" (base license)
RealTimeCollaborativeEditing (premium)enable-collab="true" + peer-dependency Yjs (self-host)
AIAssistant (premium + usage)enable-ai-toolkit="true" + provider resolver
ExportWord (premium Cloud Services)built-in DOCX endpoint + editor.aiToolkit.exportDocx()
ExportPdf (premium)html2pdf toolbar item (client-side)
Autosaverevision history auto-snapshots + form-level autosave
PasteFromOfficebuilt-in (paste-from-Word with improved list fidelity in preview.12)

Behavior differences to know about

Content model. CKEditor 5 uses a custom model tree internally and serializes to HTML. RichTextBox is HTML-native via contenteditable. For most apps the difference is invisible — both produce standard HTML. If you have a custom CKEditor 5 schema, our structured-content JSON mode is the closest equivalent.
Real-time collaboration. CKEditor’s cloud-hosted OT gives you perfect concurrent-typing with zero setup (for a monthly fee). Our Yjs textSync: true is in preview — it covers the same RFP checkbox, but caret behavior on remote apply is rougher. Per-node binding is on the 2026 roadmap.
Custom builds. CKEditor 5’s “custom build” webpack dance is gone — we ship one bundle and toggle features via Tag Helper attributes. Leave your ckeditor5-inspector and build tooling behind.

What you gain

  • Native ASP.NET Core Tag Helper — no JS wrapper, no webpack, no custom build.
  • Perpetual license vs five separate subscription SKUs (Collab, Comments, Track, Revision, AI).
  • Self-host everything, day one — including Yjs collab (your choice of WebSocket / WebRTC / Hocuspocus provider).
  • Dictation plugin, mobile toolbar mode, and public release notes out of the box.
  • Blazor Server, Web Forms, Classic ASP, PHP bindings.

Ready to switch?

Download the trial, run it on your project, and see the migration in practice.

Download trial package See pricing Full comparison

Need help with migration? [email protected]