DEV Community

Cover image for I Built a First-Class Rich Text Editor for Angular
ThomasNowHere
ThomasNowHere

Posted on Edited on Originally published at domternal.dev

I Built a First-Class Rich Text Editor for Angular

Updated September 5, 2026: this article began as a Domternal v0.2.0 build log. Domternal is now framework-agnostic, with first-party Angular, React, Vue 3 and Vanilla JavaScript integrations. The Angular motivation remains the same, while obsolete package counts, bundle figures and roadmap claims have been updated. The v1 package table and Angular guide are the maintained sources for current details.

If you've ever tried to add a rich text editor to an Angular app, you know how it goes.

Many options can put an editor on the page quickly. The harder part begins when the editor has to behave like a real Angular dependency: predictable OnPush updates, standalone components, reactive forms, typed events, theme customization and UI that stays in sync with the editing state.

I kept reaching the same point in Angular projects: the editing engine worked, but the surrounding integration still needed application-specific glue. After years of rebuilding that layer, I decided to build the editor integration I wanted to use.

What I built

Domternal is a framework-agnostic rich text editor toolkit built on ProseMirror. Its core owns the document model, extensions and commands without depending on a component framework. The Angular package adds first-party components built with Signals, OnPush, standalone APIs and reactive forms support.

Domternal now ships as 17 MIT-licensed npm packages under the @domternal scope. First-party Angular, React, Vue 3 and Vanilla JavaScript integrations all use the same core. Angular is a first-class integration, not the only framework Domternal supports.

The Angular experience I wanted

The problem was not that every alternative was bad. Different editors optimize for different constraints. Some begin with a framework-neutral JavaScript engine and add Angular bindings. Others rely on community-maintained wrappers, while commercial suites package features and licensing in different ways.

I needed a particular combination:

  • standalone Angular components with Signals and OnPush
  • ControlValueAccessor support for ngModel and reactive forms
  • extension-driven toolbars and menus that track editor state automatically
  • normal theme customization without application-specific editor overrides
  • a typed ProseMirror foundation that also works outside Angular
  • a complete Free editor that remains independently available under MIT

That combination became Domternal. The Free edition is a complete MIT-licensed editor, not a time-limited trial. Domternal Pro is a separate commercial, self-hosted layer for collaboration, comments, version history, columns, AI-assisted workflows and document export.

If you are evaluating alternatives today, the maintained Tiptap vs Domternal comparison and CKEditor vs Domternal comparison separate editor capabilities, framework integrations, hosting models and licensing without treating unlike plans as equivalent.

What makes Domternal different

6 Angular components: editor, toolbar, bubble menu, floating menu, emoji picker and Notion color picker. All use Signals, OnPush and standalone components. No NgModules are required.

Tables are free. Cell merge/split, column resize, cell styling and the cell toolbar are included in Domternal Free, with 18 table commands published under MIT.

The toolbar is extension-driven. Extensions that contribute toolbar items are discovered automatically, so their controls appear with active and disabled states without repeated button wiring in the application.

70+ extensions across core and 10 extension packages: headings, lists, code blocks with syntax highlighting, images, emoji, details, text color, font size, math, Markdown, block controls and more. Core, the four integrations, the theme and those extension packages make 17 current MIT packages in total.

JavaScript exports are tree-shakeable. The full Domternal-owned core entry is about 51 KiB minified and gzipped before ProseMirror and helper dependencies, while the current complete core bundle is about 132 KiB with those runtime dependencies. Additional extensions such as tables, images and emoji are separate packages. Actual output depends on your imports, configuration and bundler; the theme is delivered as a complete CSS stylesheet. See the maintained bundle size breakdown.

17,000+ automated test executions across unit coverage and a browser matrix for Angular, React, Vue and Vanilla. The exact current breakdown belongs in the repository and maintained documentation rather than in this historical article.

Light and dark themes with 150+ CSS custom properties. Applications can change color, spacing and typography without forking the editor styles.

TypeScript APIs. Public extensions and commands expose typed APIs and command inference without making this article a permanent claim about every internal implementation detail.

Schema conflict detection: if you accidentally register two extensions with the same name, which can happen when using StarterKit alongside individual extensions, Domternal throws a clear error instead of silently letting the last one win.

Free and Pro today

Everything described above belongs to Domternal Free unless explicitly stated otherwise. All 17 currently published packages under the @domternal scope are MIT licensed.

Domternal Pro is a separate, optional set of commercially licensed packages under the @domternal-pro scope. It adds real-time collaboration, comments, version history, columns, AI-assisted workflows and local Word/PDF export. The Pro packages run with infrastructure selected by the application and do not require a Domternal-hosted editing service.

Quick setup

Here's a minimal Angular example:

pnpm add @domternal/core @domternal/angular @domternal/theme
Enter fullscreen mode Exit fullscreen mode
import { Component, signal } from '@angular/core';
import {
  DomternalEditorComponent,
  DomternalToolbarComponent,
} from '@domternal/angular';
import { Editor, StarterKit } from '@domternal/core';

@Component({
  selector: 'app-editor',
  imports: [DomternalEditorComponent, DomternalToolbarComponent],
  template: `
    @if (editor(); as ed) {
      <domternal-toolbar [editor]="ed" />
    }
    <domternal-editor
      [extensions]="extensions"
      [content]="content"
      (editorCreated)="editor.set($event)"
    />
  `
})
export class EditorComponent {
  editor = signal<Editor | null>(null);
  extensions = [StarterKit];
  content = '<p>Hello world</p>';
}
Enter fullscreen mode Exit fullscreen mode

Add the theme import to your styles and you're done:

@use '@domternal/theme';
Enter fullscreen mode Exit fullscreen mode

See the full Angular example with all extensions, toolbar and bubble menu, or read the Getting Started guide.

No framework? No problem.

The core is fully headless and works without any framework:

pnpm add @domternal/core
Enter fullscreen mode Exit fullscreen mode
import {
  Editor, Document, Text, Paragraph,
  Bold, Italic, Underline,
} from '@domternal/core';

const editor = new Editor({
  element: document.getElementById('editor')!,
  extensions: [Document, Text, Paragraph, Bold, Italic, Underline],
  content: '<p>Hello <strong>Bold</strong>, <em>Italic</em> and <u>Underline</u>!</p>',
});
Enter fullscreen mode Exit fullscreen mode

Import the extensions you need for direct control, or use StarterKit for a batteries-included setup with headings, lists, code blocks, history and more. Your final JavaScript output depends on the imports and bundler configuration you choose.

See the full Vanilla TS example with toolbar, bubble menu and all extensions, or read the Getting Started guide.

Domternal Free today

Domternal Free
Framework integrations Angular, React, Vue 3 and Vanilla JavaScript
Angular components 6: editor, toolbar, bubble menu, floating menu, emoji picker and Notion color picker
Current MIT packages 17
Extensions 70+ across core and 10 extension packages
Nodes 27
Marks 9
Commands 130+ chainable commands
Tests 17,000+ automated test executions across maintained suites
Domternal-owned core surface ~51 KiB minified and gzipped before dependencies
Full core with current dependencies ~132 KiB minified and gzipped
Tree-shaking Actual output depends on imports, configuration and bundler behavior
TypeScript Typed public extension and command APIs
Table commands 18
License MIT

Try it now

Website: domternal.dev

Angular guide: domternal.dev/v1/guides/angular

Getting started: domternal.dev/v1/getting-started

Packages and bundle size: domternal.dev/v1/packages

GitHub: github.com/domternal/domternal

Live playground: domternal.dev/playground

Since the original v0.2.0 article

React and Vue integrations, math, drag handles, block controls and other features that were once described as future work now exist in the maintained v1 documentation. Future requests can inform product planning, but neither this historical post nor a request is a delivery commitment.

The complete Free editor remains MIT licensed. The optional Domternal Pro packages now add collaboration, comments, version history, columns, AI-assisted workflows and Word/PDF export for teams that need those workflows while retaining control of their application infrastructure.

The current release status, supported packages and installation instructions are published in the v1 documentation. I would still appreciate feedback on the API design, documentation or anything that could be better.

What's been your biggest pain point with rich text editing in Angular? I'd love to hear about it in the comments.

Top comments (0)