Developing an HTML editor in React.js (WYSIWYG)

These past few weeks, I’ve been exploring different Markdown editors for a side project. It all started as a simple text-area
where I manually copied the Markdown I edited in some other editor (Obsidian, Notion, Dillinger). Then, I tried adding a WYSIWYG preview ("What You See Is What You Get"), and I ended up exploring different options to avoid reinventing the wheel: ProseMirror/CodeMirror (used by TipTap), Ace (which seems abandoned), Draft.js (deprecated), and Lexical.
While researching, I found that Obsidian uses CodeMirror internally, Dillinger.io uses Ace (which hasn’t been updated in 7 years—it's still using Node 16). Then there’s Lexical by Meta, which seems super powerful but lacks proper documentation, making development a real headache. After reading a lot, the best option is to use TipTap, which is clearly well-maintained by a dedicated company. The editor is open-source and runs on CodeMirror / ProseMirror, which at least have more documentation available.
After several days of reading and developing, I found it interesting to study how these editors work internally. It turns out there are mainly two approaches:
Using a "hidden"
textarea
that acts as a proxy to adiv
. The textarea handles input/output but has the limitation of not allowing styles or colors, so a div is used to display the content.Using a
div
with the attributecontenteditable=true
. This allows the browser to capture all necessary events, which we then intercept and manage in the component state: every time a key is pressed, we compute text modifications, render them, then transpile to HTML and inject them into the div for rendering.
Sounds easy, but trust me, it’s not. There’s a lot of complexity in different areas, though it’s super interesting. So, why not give it a shot? Curiosity always drives me to experiment with these kinds of things.
During college, I loved the compilers course, where we built our own syntax that compiled to C++. Since then, I’ve been fascinated by automata, lexers, parsers... Plus, it's a highly relevant topic in frontend development today, especially with Rust initiatives like SWC/Turbopack on one side, and void0 working on RollDown and Vite 6, or TikTok's RsPack. But anyway, back to the main topic.
These past few days, I’ve been working on a proof of concept. It’s way more complex than it looks and requires a ton of work: What data structure should be used? How should HTML events be managed? Is it better to maintain an internal state and render it later, or not?
I found it interesting to upload the editor code to a GitHub repository in case anyone else wants to experiment with it. Right now, it's just a PoC, with some keys not implemented yet and a few edge cases to handle properly. I don’t plan to continue working on it for now.
Did you find this article useful?Join the community to receive exclusive content!