Buying

Should you build your own rich text editor?

Sometimes yes. The honest version of this question isn't “can you” — a usable toolbar is a weekend. It's whether you want to own the twelve problems that arrive in year two, and most of them are invisible from year one.

12 min read

We sell an editor, so treat this with the scepticism it deserves. But the honest answer is not “never build one” — it is that the decision hinges on something most teams do not evaluate until they are eighteen months in.

Getting to a working editor is genuinely easy. contenteditable plus document.execCommand plus a toolbar is an afternoon. That demo is why so many teams start, and it is not a trap in itself. The trap is that the demo teaches you nothing about what the next two years contain.

The real question is not difficulty

Rich text editing is not conceptually hard. It is wide. The work is not one difficult problem, it is several dozen small ones that each look like an afternoon and each turn out to have a subtlety that costs a week. And crucially, they do not arrive at the start. They arrive one at a time, from users, after you have already shipped.

Below is that list, grouped by when it usually shows up.

Month one: the part everyone budgets for

This part is real work but it is predictable, and if this is all you need — a comment box, a bio field, a changelog entry — build it. Pulling in a full editor for a comment box is the opposite mistake, and a real one. See the last section.

Month six: the part that surprises people

Paste.Users paste from Word, Google Docs, Excel, other web pages and Outlook. A paste from Word can carry 40 KB of markup for a single sentence — mso- styles, conditional comments, nested empty spans, list structure encoded as paragraph classes. You need a filter that discards presentation without discarding the structure the author cared about. That is a genuine engineering project, not a regex.

Undo.The browser gives you a native undo stack that breaks as soon as you modify the DOM yourself, which every non-trivial feature does. So you write your own — and now every feature must cooperate with it, and undo must merge consecutive typing but not merge a typing run with a table insert.

Selection and ranges. Applying formatting to a selection that spans three block elements and starts mid-word is not a one-liner. Neither is preserving the caret across a DOM mutation. We wrote about one instance of this in why your format painter silently does nothing — the bug there is one line of the CSS cascade almost nobody thinks about.

Tables. Users expect Word-grade tables: merged cells, tab navigation, column resizing, row insert relative to the caret. Merged cells alone will occupy someone for a fortnight.

Year two: the ones that get you audited

These are the items that rarely appear in a build-versus-buy spreadsheet, and they are the expensive ones.

XSS.Your editor produces a string that another user’s browser renders. That is the classic stored-XSS shape. Stripping <script> and on* handlers is the beginning: you also need to handle javascript: URLs with control characters embedded in the scheme, data:image/svg+xml (an SVG is a document and a document can carry script), <iframe srcdoc>, CSS url() exfiltration, and mutation XSS where markup is inert when parsed and dangerous once re-serialised.

We shipped an editor for years with an iframe srcdoc hole that executed downstream, and we found it by testing our own output rather than our own editor. The full account is in “nothing executes in the editor” is the wrong bar. If you build, this is now permanently your problem, and it is the one with a CVE at the end of it.

Accessibility. Not a checkbox — a set of behaviours. Toolbar buttons need aria-pressed that mirrors real state. Menus need aria-expanded. The toolbar should be one tab stop with arrow-key navigation, not forty tab stops. And the one that catches nearly everybody: Tab has to indent lists and also let keyboard users leave, which is a direct conflict and a WCAG Level A failure if you get it wrong. We had that trap too; the Tab key problem in contenteditable covers why both obvious fixes are wrong. If you sell to government or education, this is procurement-blocking.

Word import and export. “Export to Word” usually means HTML in a .doc wrapper, which Word opens and nags about. A real .docx is a ZIP of XML with a relationship graph, where every hyperlink is an indirection, list type lives in a separate part, w:sz is in half-points, and one control character makes Word reject the whole file. See what is actually inside a .docx.

PDF export. The easy path renders each page to a canvas, producing a picture of a document: unselectable, unsearchable, invisible to screen readers. The correct path means emitting the PDF format, and if accessibility matters, a full structure tree on top. See a searchable PDF and an accessible PDF are different features.

Collaboration. If two people ever edit the same document, you need CRDTs or OT, presence, cursor mapping through remote edits, and offline reconciliation. This is the one item on this list that is genuinely hard rather than merely wide.

The cost nobody estimates: it never finishes

A bought editor has a version number. A built editor has a team. Browsers change contenteditablebehaviour, Safari does something different, a Chrome update alters how a selection normalises across an inline boundary, and none of it appears in your test suite because your tests assert on your code rather than on the browser’s.

The honest unit is not “how long to build” but “how much engineering capacity, permanently”. For anything approaching a document editor, that is not zero and it never returns to zero.

When building is the right call

Genuinely, and we would rather you did than buy something you resent:

When buying is the right call

The middle path most teams miss

It is not binary. A headless editor — where the vendor owns the editing engine, serialisation, paste handling and accessibility, and you own every pixel of UI — is the right answer more often than either extreme. You get the twelve invisible problems solved and still control the interface completely.

If you take one thing from this: decide based on year two, not the prototype.The prototype always works. Write down what happens the first time somebody pastes from Word, the first time a screen reader user files a ticket, and the first time legal asks for a VPAT. If those answers are comfortable, build it — you will build something better suited to you than anything you could buy.

If they are not, the question is not whether you could solve them. It is whether solving them is what you want your engineers doing.


RichTextEditor is a perpetual-licence JavaScript editor — one purchase, self-hosted, no metered editor loads. Download the evaluation or see how it compares.