WYSIWYG editing Image gallery upload Content templates
Migration guide

Migrate from TinyMCE or CKEditor — in under a day.

Existing HTML content keeps working. Toolbar strings map mostly 1→1. Every premium-tier feature you’re paying for today — AI Toolkit, Track Changes, Comments, Revision History, Mentions — ships in our base licence. Below: side-by-side config cheat sheets, plugin equivalence tables, and a step-by-step checklist.

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

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.

Why teams move

  • Lower cost. Froala’s commercial license is per-project. We’re $129 once for a perpetual domain license that includes Track Changes, Comments, Revision History, and the AI Toolkit — features Froala either lacks or charges extra for.
  • Wider feature set. Froala doesn’t ship Track Changes or Threaded Comments at all; our v2.0 stack includes both in the base license.
  • First-class .NET integration. Froala has React/Vue/Angular wrappers but no idiomatic ASP.NET Core Tag Helper. We’re a one-line <richtextbox> tag.
  • BYOK AI without a proxy. Froala’s AI Assist plugin requires routing through your backend; our resolver pattern is one-line DI registration.

Step-by-step checklist

  1. Install the NuGet: dotnet add package RichTextBox.AspNetCore --version 1.0.0-preview.13
  2. Register in Program.cs: builder.Services.AddRichTextBox(); · app.MapRichTextBoxUploads();
  3. Drop RichTextBox.lic next to Program.cs.
  4. Replace new FroalaEditor('#editor', { ... }) with the <richtextbox asp-for="Body" toolbar="full" /> Tag Helper.
  5. Map your Froala toolbarButtons to our toolbar attribute — see cheat sheet below.
  6. If you use Froala AI Assist, register an IRichTextBoxAiResolver: builder.Services.AddRichTextBoxOpenAiResolver(opts => ...).
  7. Replace any custom event listeners with editor.attachEvent("change", fn) equivalents.
  8. Existing HTML keeps working; submit forms as normal.

Config cheat sheet

Basic setup

Froala

new FroalaEditor('#editor', {
  toolbarButtons: [
    'bold', 'italic', 'underline', '|',
    'formatOL', 'formatUL', '|',
    'insertLink', 'insertImage'
  ],
  height: 500
});

RichTextBox Tag Helper

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

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

AI (Froala AI Assist → RichTextBox AI Toolkit)

Froala AI Assist (5.1+)

FroalaEditor.DefineIcon('aiAssist',
  { NAME: 'magic', SVG_KEY: 'magicWand' });
FroalaEditor.RegisterCommand('aiAssist', {
  callback: function () {
    fetch('/my-proxy', { ... })
      .then(/* manual integration */);
  }
});

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" />

Image upload (custom upload URL)

Froala

new FroalaEditor('#editor', {
  imageUploadURL: '/api/upload-image',
  imageUploadParam: 'file'
});

RichTextBox (built-in endpoint)

// Program.cs maps /richtextbox/upload automatically:
app.MapRichTextBoxUploads();

// Want to send images to S3 / Azure / Cloudinary instead?
// Implement IRichTextBoxUploadStore — see /UploadProviders.

Plugin equivalence

Froala plugin / buttonRichTextBox equivalent
bold, italic, underlinebuilt-in (bold, italic, underline)
fontFamily, fontSizebuilt-in (fontname, fontsize)
colorbuilt-in (forecolor, backcolor)
formatOL, formatULbuilt-in (insertorderedlist, insertunorderedlist)
insertLink, insertImage, insertVideo, insertTablebuilt-in
emoticonsbuilt-in (insertemoji)
specialCharactersbuilt-in (insertchars)
codeViewbuilt-in (code)
fullscreenbuilt-in (fullscreenenter, fullscreenexit)
printbuilt-in (print)
spellCheckerbrowser-native + spellcheck toolbar toggle
saveauto-save="true" + auto-save-key + auto-save-delay
filebuilt-in (insertdocument) + MapRichTextBoxUploads()
wordCountnative via editor.getText().length
Track Changesenable-tracked-changes="true"Froala doesn’t ship this
Commentsenable-comments="true"Froala doesn’t ship this
Revision Historyenable-revision-history="true" — Froala has only basic undo
AI Assist (5.1+)enable-ai-toolkit="true" + provider resolver
Word exportbuilt-in DOCX endpoint + editor.aiToolkit.exportDocx()
PDF exporthtml2pdf toolbar item (client-side)

Behavior differences to know about

HTML output. Froala adds a few framework-specific attributes (class="fr-fic", etc.) for image alignment / floats. Our output uses standard inline styles. For most apps the swap is invisible to downstream consumers.
Events. Froala’s events: { 'contentChanged': fn }editor.attachEvent("change", fn).
iframe vs. div. Froala uses a contenteditable div inside the host page. Our editor uses an iframe by default for content isolation; pass config.iframe = false to match Froala’s in-page behaviour.

What you gain

  • Track Changes + Comments + Revision History — Froala doesn’t ship any of these.
  • Slash commands & @mentions — Notion-style inline picker; Froala has no equivalent.
  • Yjs real-time presence + concurrent typing preview.
  • Dictation (Web Speech API microphone button).
  • ASP.NET Core Tag Helper, Blazor, Web Forms, Classic ASP, PHP bindings.
  • Pluggable cloud upload providers (S3, Azure Blob, Cloudinary, GCS) via one DI registration.
  • Perpetual one-time license at $129 vs Froala’s per-project commercial pricing.

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]