DEV Community

Cover image for I built a rich-text editor from scratch. No ProseMirror, no dependencies.
Aditya Bhalsod
Aditya Bhalsod

Posted on

I built a rich-text editor from scratch. No ProseMirror, no dependencies.

I built a rich-text editor from scratch. No ProseMirror, no dependencies.

Almost every rich-text editor you can install today is a layer over one of a handful of engines: ProseMirror, Lexical, Slate, Quill, Draft.js. That is a sensible way to build one. I wanted to find out what happens if you don't.

So I wrote Trevixal: a WYSIWYG editor engine in about 60,000 lines of TypeScript and SCSS, with the document model, the undo system, the HTML sanitizer, the DOM renderer, the ZIP and XML writers behind Word export, and the LaTeX-to-MathML converter all written in this repository. It is Apache-2.0, and no package it ships has a runtime dependency.

Try it before you read any further: the live editor. Paste something ugly into it and see what happens. That is the best test I know.

The full editor: menubar, toolbar, outline and a document

It is a building block, not an app

Trevixal is not a finished product you send people to. It is the text box you put inside your own product (a CMS, a support desk, a notes tool, an internal wiki), so your users get the writing surface they expect from Google Docs or Notion while your app keeps the document, the storage and everything around it.

That shapes how it is built. The core is headless. The chrome (menus, toolbar, dialogs) is a separate, optional package. There are adapters for React, Vue, Svelte and Angular, a web component, and an assembled "whole editor" you can mount in one call.

Three decisions that shaped everything

1. The document is data, not HTML. Every document is a schema-validated JSON object you can store in a database, validate, transform and render anywhere. The editor on screen is one view of it. A .docx, an .rtf, a web page or a PDF are others.

2. Every edit is a list of small, invertible steps. Undo is not a second bookkeeping system bolted on later; it falls out of inverting the steps. The same steps remap positions through a transaction, so selections do not drift when something changes above them.

3. Pasted HTML is hostile until proven otherwise. Content from the web is parsed inside an inert template and passed through an allowlist, and the sanitizer is tested against a corpus of real attack payloads.

None of these is novel on its own. The interesting part was that owning all three at once let me fix whole classes of bugs in one place instead of working around someone else's engine.

What the editor does

Everything below is reachable from the editor's own menus and toolbar, and every screenshot in this post was taken from the real demo, not a mock-up.

Type / at the start of a block and the insert menu opens: headings, lists, quotes, code, tables, images, diagrams, equations, callouts, columns and toggles.

The slash menu opened on an empty line

Tables are real tables: click a cell for its own toolbar, drag a border to resize, merge and split cells in both directions, and use Word-style formulas such as SUM and AVERAGE over ABOVE, BELOW, LEFT or RIGHT.

A table with a nested table inside a cell, a numbered caption and right-aligned costs

Equations are LaTeX, rendered as MathML. Double-click one to edit it with a symbol palette and a live preview.

Inline and block equations, with an automatic equation number

Diagrams are code blocks that draw themselves. Mermaid, Graphviz and PlantUML all work, and the picture lands in your exports too.

A Mermaid flowchart rendered under its source, followed by tabs and accordions

Review works like Word's suggesting mode. Switch it on, and edits arrive as attributed suggestions that someone can accept or reject, one at a time or all at once.

Suggesting mode: a reviewer's edit highlighted, with Accept and Reject controls

There are seven built-in themes (Light, Dark, Sepia, Nord, Solarized, High contrast and Midnight), an option to follow the system setting, and you can define your own.

The same document in the Dark theme

And it exports. The File menu writes the document as HTML, Markdown, MDX, plain text, JSON, Word, RTF, OpenDocument, EPUB, LaTeX, PowerPoint, a fillable PDF, a PDF through print, or a password-encrypted .tvx file. The exports are checked by rendering the real files, not by reading the markup, so a downloaded document looks like the editor did.

The File menu listing every download format

Using it takes three lines

The no-build version is one HTML file. Save this and open it:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>My Web Page</title>
    <link rel="stylesheet" href="https://unpkg.com/@trevixal/ui/styles.css" />
    <link rel="stylesheet" href="https://unpkg.com/@trevixal/editor-kit/styles.css" />
    <script src="https://unpkg.com/@trevixal/editor-kit"></script>
  </head>
  <body>
    <div id="app"></div>
    <script>
      TrevixalKit.mountFullEditor({ element: document.querySelector('#app') })
    </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

For a real app you pin a version in those URLs. If you want the engine without any chrome, it is a few lines of TypeScript:

import { Schema, createEditor, defaultNodes, defaultMarks } from '@trevixal/core'
import '@trevixal/ui/styles.css'

const editor = createEditor({
  schema: new Schema({ nodes: defaultNodes(), marks: defaultMarks() }),
  element: document.querySelector('#editor'),
  placeholder: 'Write something…',
})

editor.commands.toggleMark('bold')
const doc = editor.getJSON() // the document, as data you can store and validate
Enter fullscreen mode Exit fullscreen mode

How it compares to Tiptap

Tiptap is the editor most people compare this one to, and the comparison is fair: both are headless, extension-driven and have adapters for the major frameworks. The real difference is that Tiptap is a layer over ProseMirror and Trevixal is not a layer over anything.

I measured both on 19 September 2026, using the smallest install on each side that gives an editor with a default schema, bundled the same way:

Trevixal Tiptap
Engine + default schema (gzipped) 27.3 kB 117.2 kB
npm packages installed 1 42
ProseMirror packages 0 13
Track changes In the box Paid extension
DOCX / RTF export In the box Paid extension or your own
Collaborative editing No Yes, first-class
Production maturity New (1.0.0, September 2026) Years

I would pick Tiptap if you need collaborative editing, you are shipping on a deadline and want years of hardening, you want a large community and Stack Overflow answers, or you want commercial support. Those are real advantages and none of them are close.

I would pick Trevixal if you want to read the whole stack in one repository, your supply chain makes dependency count a constraint, you want Word export and track changes without a paid tier, or you need an editor that runs from one <script> tag.

What it deliberately does not do

Trevixal is a single-user editor. I built real-time collaboration, measured it, and removed it on 12 September 2026; the decision is recorded as an architecture decision record in the repository. There is no live co-editing, so if that is your requirement, use something else. Comment threads on a selection are available as an optional extension as of 1.1.0, saved in the document and written to Word files as Word comments. It is also a young project: the first release was in September 2026. Treat it that way.

What 1.1.0 added

The latest release is built around long documents and real Word interop: heading numbering in four schemes, numbered captions with cross-references, tables of figures, endnotes and an index, named styles, drop caps, columns and tab stops, merge-and-split tables, comments, fillable forms with mail merge, and a scaffold, npm create @trevixal/extension <name>, for writing your own extension. The additions are backwards compatible: nothing was removed, and every new option is optional.

The numbers, for the skeptics

  • 26 packages under @trevixal/*: a headless core, an optional UI chrome, an assembled editor, five adapters (React, Vue, Svelte, Angular, web component) and seventeen extensions.
  • 3,051 unit tests across 212 files, and 333 browser tests across 48 specs, run on Chromium, Firefox and WebKit.
  • Zero runtime dependencies in any published package.
  • Apache-2.0.

If you try it

The most useful thing you can do is break it. Open the live editor, paste the ugliest thing you have (a Word document, a Google Doc, a web page full of inline styles), and open an issue with what went wrong. Paste handling is the hardest part of any editor, and real-world input is what finds the bugs.

If Trevixal looks useful, or just interesting, a star on GitHub is the cheapest way to help other developers find it. It is a small project and every star is noticed.

Thanks for reading. I would genuinely like to hear what you would build with it.

Top comments (0)