Rebuilding a web text editor
This text was written by Readymag chief technology officer Ilya Medvedev and originally published on the Readymag blog on DEV Community.<br>Back in 2020, I was working on building a text editor at Readymag, an online design tool that helps people create websites without coding. It was a complex but rewarding journey that resulted in a tool that met all our requirements at the time. While we were pleased with many of the architectural choices, software engineering teaches us that there’s always room for improvement.<br>As we at Readymag began planning to build our next text editor, revisiting those earlier decisions—both the successful and the limiting ones—became essential. This post is about what we found and how it’s shaping what comes next.<br>Bottlenecks of the previous solution<br>Readymag is a design tool for creating websites—we like to say it's like a website builder, but better, because it has no layout restrictions. It's widely used by designers for projects where visual impact matters most: both for the result, and the experience. Think portfolios, landing pages, presentations, and marketing campaigns where pixel-perfect design control is essential.<br>I've been working at Readymag for seven years, and since publishing my previous article, I've moved from lead engineer to chief technology officer. This shift gave me a wider perspective, the possibility, or even responsibility, to improve product functionality and approaches.<br>Our old text editor was part of the legacy codebase, present almost since Readymag’s launch in 2013. Over time, changes became increasingly difficult to implement, and introducing new features—such as support for variable fonts—was simply out of reach.<br>The first text widget in Readymag (2013)That was the reason we decided to build a new text widget. You all know that working with text on the web is a thankless job—browsers handle text differently, users expect native behavior in web environments, and seemingly simple features like "delete a word" become complex when you consider international character sets or emoji sequences.<br>Therefore, we wanted to choose a low-level framework that would solve most of the issues related to text input. We settled on Draft.js, which was quite popular at the time (2020). All we had to do was integrate it into our current system, attach it to the data storage, and implement the ability to edit styles with our constructor—done.<br>But what was wrong with that idea?<br>1. Lack of control<br>At Readymag, we develop our own design tool. We have our own data structures, many in-house developments, and we need maximum control over the browser and low-level interactions. At the same time, Draft.js has had a bug since 2017: it incorrectly handles emoji sequences. Some emoji can be longer than 1 character; for example, 👨🏼🎨 — has a length of 7, not 1 as it appears on screen. Draft.js incorrectly processes such sequences, which can lead to errors when inserting or deleting text.<br>Here’s where it gets interesting. We can create an issue, make our own fix and send a pull request (after all, this is the open source world, and we need to contribute), we can make a monkey patch, etc. But all these options slow down processes and complicate control over functionality. And text is the most popular widget in Readymag—this isn’t something we can neglect.<br>2. Lack of confidence<br>Draft.js received its last commit a couple of weeks before my previous article. Today, it’s a public archive. You can't blame anyone here—again, this is an open source world, it's normal. But this means that in addition to vendor lock, we got an uncertain future for our text widget.<br>This uncertainty becomes particularly painful when you're building a commercial product. Your roadmap depends on features that may never come, security updates that may never arrive, and browser compatibility fixes that someone else needs to prioritize. Meanwhile, web standards continue evolving—new APIs emerge, browser behaviors change, and user expectations grow. When your foundation stops moving forward, you're essentially betting your product's future on code that's frozen in time. The technical debt accumulates not just from what you build on top, but from the growing gap between what your dependency supports and what the modern web offers.<br>3. Third-party libraries<br>There are many pros and cons to both approaches. The common assumption is: “Isn’t it faster and easier to just take a library and plug it in?” In reality, that’s not always the case—it depends entirely on your situation. For us, it made far more sense to develop our own engine rather than reuse someone else’s, because this is a critical part of the product.<br>If you look at the time spent on integration and the extra code added to the codebase beyond simply installing a third-party package, the numbers can be surprising—months or even years of work, and thousands of additional lines of code.<br>4. Principle of least astonishment<br>And last but...