Engineering
Why I Started Building Lextrix
Not a pitch — how reading Quill source, hitting extension walls, and building the wrong monolith first led to an open-source TypeScript editor ecosystem.
I did not set out to build a rich-text editor.
I set out to use one. Quill worked fine for what I needed at first — toolbar, bold, lists, paste from Word without completely destroying the layout. Then requirements grew. Custom embeds. Stricter undo behaviour. A theme that did not fight our design system. And I kept hitting walls where the fix was either a fragile DOM hack or a deep dive into Quill internals I did not fully understand.
That is when I started reading Quill's source code instead of Stack Overflow answers. I wrote about that separately in Reading Quill Source Code. The short version: once you see how a document model, change operations, and blot registry fit together, you cannot unsee it. You also start noticing where your own mental model is fuzzy.
Lextrix came from that fuzziness. Not from a pitch deck. Not because I thought the world needed another editor. I wanted to understand editors properly, in TypeScript, with package boundaries I could explain to myself six months later.
The problem I actually had
Most projects do not need a custom editor on day one. Contenteditable and a contentEditable div gets you surprisingly far if you are careful. Libraries like Quill, TipTap, and ProseMirror exist because "careful" does not scale.
My pain points were boring but real:
- Extension without fork. I wanted to register formats and modules without copying vendor files into our repo.
- Predictable changes. Undo/redo broke in edge cases when HTML and internal state drifted apart.
- TypeScript. I wanted types at the boundaries — document ops, selection ranges, module config — not just
@typeshope. - Ownership. When something broke at 11pm before a release, I wanted a codebase I could trace, not a minified bundle and prayer.
None of that is unique. Every team that outgrows a basic textarea hits some version of this list. What was different for me: I had time outside work, and I had already burned a weekend reading Quill. Building my own thing felt less insane than it would have a year earlier.
Why not just switch libraries?
I looked at TipTap and ProseMirror seriously. ProseMirror is excellent — arguably the gold standard if you want a document model done right. TipTap gives you a productive React layer on top.
I did not pick them for Lextrix not because they are bad, but because my goal was not "ship an editor this sprint." My goal was "learn the substrate." ProseMirror would have taught me a lot, but I would have been learning ProseMirror's architecture, not building muscle for designing my own module graph.
That sounds precious. Maybe it is. But side projects die when they are only about the outcome. This one stayed alive because the process was the point — with the hope that something useful would come out the other end.
Also, honestly: I am stubborn. Once I had sketched a package layout on paper, I wanted to see if I could make it work.
The first wrong version
My first attempt was a monolith. One repo, one bundle, lextrix.ts that did everything — DOM sync, keyboard shortcuts, toolbar, themes, clipboard sanitisation. It worked in a demo page. Then I tried to extract "just the change layer" for a test and realised I had coupled everything through a shared mutable editor instance.
Classic mistake. I had read about separation of concerns in Quill's blot/module split and still built a god object because it was faster for the weekend demo.
Throwing that away hurt. But it clarified the boundary I actually wanted:
- Document + DOM — blots, attributors, registry, browser integration
- Change processing — operations, compose, invert, transform (for undo and future collab)
- Editor shell — selection, commands, lifecycle, plugin host
- Modules — clipboard, keyboard, toolbar, syntax, image resize
- UI — optional toolbar widgets for teams that want React components without forking internals
That maps to what became lextrix-dom, lextrix-change, lextrix-core, lextrix-modules, lextrix-formats, lextrix-ui, and lextrix-themes, with a published lextrix bundle on npm for people who want the batteries-included install.
Naming the packages took longer than I expected. I renamed things twice. If you browse early commits, you will see evidence of my indecision. That is fine. Public OSS means your naming mistakes are archived forever. Welcome to the club.
What Lextrix is trying to be
Lextrix is an open-source modular TypeScript rich-text editor ecosystem. That sentence is careful on purpose.
Modular — you can depend on lextrix-change alone if you only want the operation layer, or ship the full bundle if you want a Quill-like getting-started path.
TypeScript-first — not "types bolted on later." The public APIs are written TS-first even if some internals still carry scars from the JS prototype phase.
Ecosystem — I do not think a single npm package is enough for editors. Editors touch DOM, input events, clipboard, IME, mobile keyboards, accessibility. One package always becomes a junk drawer.
What Lextrix is not: a finished Quill killer. Version 2.0.0 on npm is real and usable — including HTML, Markdown, and MDX serialization — but the public playground, framework starters, and stable MDX component handlers are still on the roadmap. I would rather say that plainly than pretend otherwise.
Technical bet: ChangeSets
Quill uses Deltas — insert/retain/delete operations on a document. Lextrix uses a ChangeSet layer with compose, diff, invert, and transform utilities. Same family of idea, different naming and implementation details shaped by what I needed while building.
I chose to make change processing explicit because undo/redo kept being the canary in the coal mine. If you cannot invert an operation reliably, your editor will gaslight users. "I pressed undo why did my list disappear?"
Getting invert right for nested formats taught me more about the document tree than any tutorial. I still have edge cases filed as GitHub issues. That is not a fake humble brag — go look at the repo.
Open source because it has to be
Lextrix is MIT licensed on GitHub. Practical reasons:
- I used open-source editors for years without paying forward. This is partial repayment.
- If nobody can read the code, nobody can trust it for anything beyond a toy demo.
- Issues and PRs force me to explain decisions in writing — which makes the architecture better.
Selfish reason: public repos are my portfolio now. Recruiters can read code instead of another CRUD tutorial.
Mistakes I am still living with
Documentation lag. The code moved faster than docs for stretches. npm READMEs are okay; deep architecture docs are still catching up. That hurts adoption and I know it.
Scope creep in modules. Every module "just one more feature" adds maintenance surface. I had to cut a syntax module variant because it was half-working and confusing.
Testing gaps. Unit tests exist for change logic and parts of the DOM layer. Full browser integration tests are thinner than I want. Editors are event-heavy; Vitest alone is not enough long term.
Marketing instinct. I am an engineer, not a devrel person. I built Lextrix before I wrote this post. That order was backwards for discoverability. Fixing that now.
Why this strengthens how I work elsewhere
Building Lextrix changed how I read other people's abstractions. Framework docs feel different when you have tried to implement the layer underneath. Code review comments get more specific. "Can we make this operation invertible?" is a real question I ask now.
It also changed my patience for innerHTML shortcuts. Sometimes they are fine. Often they are debt with interest.
If you are thinking about building an editor
Do not start here unless you mean it. Use ProseMirror, TipTap, Quill, Slate — battle-tested tools built by people who spent years on edge cases you have not imagined yet.
One edge case example: IME composition for CJK input. I have not solved that fully in Lextrix yet. Quill and ProseMirror teams have years of bug reports on composition boundaries. That is not a reason to avoid building — it is a reason to be honest about maturity.
Start here only if:
- You want to learn document models properly
- You have time measured in months, not days
- You can tolerate being unknown for a long time
If that is you, read existing source first. Ship a tiny vertical slice — one format, one module, undo that works — before you design a theme system. I did it backwards once. Learn from that.
Where Lextrix goes next
Public playground. React starter template. More cookbook examples for custom blots and clipboard filters. Bundle size benchmarks documented honestly. Maybe collaboration examples built on transform — not because v1 needs it, but because it proves the change layer is real.
If any of that sounds useful, try npm install lextrix, open an issue, or tell me what broke. I respond faster to bug reports than to "great project" tweets. Both are welcome. Bugs more so.
I started building Lextrix because I did not understand editors well enough. I am still building it for the same reason — just with more code and fewer illusions.
If you read one other post on this site for context, make it What Reading Quill's Source Code Taught Me About Editors. That is the technical prelude to this one.