Classic Notion-style Headless Toolkit

The Rich Text Editor
your framework
deserves

Classic and Notion-style editors out of the box.
Or go headless and assemble from 65+ tree-shakeable extensions.
First-class wrappers for Angular, React, Vue, and Vanilla.

15,000+
Tests
65+
Extensions
~44 KB
own code
100%
TypeScript
Tree-shakeable Classic + Notion modes Headless toolkit Angular · React · Vue · Vanilla MIT licensed Tables with merge Slash command Markdown shortcuts Drag & drop blocks Block context menu Light + dark theme Image upload SSR ready Built on ProseMirror Brushed to spec Tree-shakeable Classic + Notion modes Headless toolkit Angular · React · Vue · Vanilla MIT licensed Tables with merge Slash command Markdown shortcuts Drag & drop blocks Block context menu Light + dark theme Image upload SSR ready Built on ProseMirror Brushed to spec Tree-shakeable Classic + Notion modes Headless toolkit Angular · React · Vue · Vanilla MIT licensed Tables with merge Slash command Markdown shortcuts Drag & drop blocks Block context menu Light + dark theme Image upload SSR ready Built on ProseMirror Brushed to spec Tree-shakeable Classic + Notion modes Headless toolkit Angular · React · Vue · Vanilla MIT licensed Tables with merge Slash command Markdown shortcuts Drag & drop blocks Block context menu Light + dark theme Image upload SSR ready Built on ProseMirror Brushed to spec
Tree-shakeable Classic + Notion modes Headless toolkit Angular · React · Vue · Vanilla MIT licensed Tables with merge Slash command Markdown shortcuts Drag & drop blocks Block context menu Light + dark theme Image upload SSR ready Built on ProseMirror Brushed to spec Tree-shakeable Classic + Notion modes Headless toolkit Angular · React · Vue · Vanilla MIT licensed Tables with merge Slash command Markdown shortcuts Drag & drop blocks Block context menu Light + dark theme Image upload SSR ready Built on ProseMirror Brushed to spec Tree-shakeable Classic + Notion modes Headless toolkit Angular · React · Vue · Vanilla MIT licensed Tables with merge Slash command Markdown shortcuts Drag & drop blocks Block context menu Light + dark theme Image upload SSR ready Built on ProseMirror Brushed to spec Tree-shakeable Classic + Notion modes Headless toolkit Angular · React · Vue · Vanilla MIT licensed Tables with merge Slash command Markdown shortcuts Drag & drop blocks Block context menu Light + dark theme Image upload SSR ready Built on ProseMirror Brushed to spec
FEATURES

Everything you need to build
rich editing experiences

A complete editor framework with full control over every aspect of your editor.

01

65+ Built-in Extensions

23 nodes, 9 marks, 27 extensions across 15 tree-shakeable packages. Tables with merge, images, emoji, mentions, details, syntax highlighting, Notion-style blocks, and table of contents.

02

Angular, React, Vue & Vanilla Wrappers

Drop-in editor, toolbar, bubble menu, floating menu, emoji picker, and Notion color picker. Or skip them - use the headless core with any framework.

03

Built-in Toolbar & Theme

Each extension contributes toolbar items via addToolbarItems(). 49 Phosphor icons. Light and dark themes via CSS custom properties - restyle with one declaration.

04

Styled HTML Export

getHTML({ styled: true }) outputs inline CSS that survives email clients, CMS rich-text fields, and Google Docs paste. No external stylesheet required.

05

Markdown + Slash Commands

Type ## for headings, **bold**, > for blockquote, --- for divider, or / to open the slash menu. Input rules registered by every extension.

06

Notion-style Block UX

Hover to grab a block, drag to reorder. Right-click for duplicate, turn into, color, or copy link. Tab to indent. Toggle the mode on with .dm-notion-mode.

EXTENSION ECOSYSTEM

Every extension you need.

Batteries included: from basic formatting to advanced features.

BoldItalicUnderlineStrikethroughCodeLinkSubscriptSuperscriptHighlightText ColorBoldItalicUnderlineStrikethroughCodeLinkSubscriptSuperscriptHighlightText ColorBoldItalicUnderlineStrikethroughCodeLinkSubscriptSuperscriptHighlightText ColorBoldItalicUnderlineStrikethroughCodeLinkSubscriptSuperscriptHighlightText Color
Font FamilyFont SizeLine HeightClear FormattingHeadingParagraphBlockquoteCode BlockSyntax HighlightingHorizontal RuleFont FamilyFont SizeLine HeightClear FormattingHeadingParagraphBlockquoteCode BlockSyntax HighlightingHorizontal RuleFont FamilyFont SizeLine HeightClear FormattingHeadingParagraphBlockquoteCode BlockSyntax HighlightingHorizontal RuleFont FamilyFont SizeLine HeightClear FormattingHeadingParagraphBlockquoteCode BlockSyntax HighlightingHorizontal Rule
Bullet ListOrdered ListTask ListTableImageEmojiMentionDetailsHistoryText AlignTypographyBullet ListOrdered ListTask ListTableImageEmojiMentionDetailsHistoryText AlignTypographyBullet ListOrdered ListTask ListTableImageEmojiMentionDetailsHistoryText AlignTypographyBullet ListOrdered ListTask ListTableImageEmojiMentionDetailsHistoryText AlignTypography
PlaceholderCharacter CountBubble MenuFloating MenuDropcursorGapcursorTrailing NodeInvisible CharactersUnique IDStarter KitFocusPlaceholderCharacter CountBubble MenuFloating MenuDropcursorGapcursorTrailing NodeInvisible CharactersUnique IDStarter KitFocusPlaceholderCharacter CountBubble MenuFloating MenuDropcursorGapcursorTrailing NodeInvisible CharactersUnique IDStarter KitFocusPlaceholderCharacter CountBubble MenuFloating MenuDropcursorGapcursorTrailing NodeInvisible CharactersUnique IDStarter KitFocus

65+ extensions across 15 packages. All MIT licensed.

View All Packages →
UNDER THE HOOD

Built for production

Architecture decisions that make the difference at scale.

01

Schema Conflict Detection

Duplicate extension names throw a clear error at startup. No mysterious production bugs from silent overwrites.

02

Extension Composability

create() → configure() → extend() with this.parent?.() to call the base version. Override any hook while preserving parent behavior.

03

Fluent Command API

editor.chain().focus().toggleBold().run() chains commands on a shared transaction. editor.can().toggleBold() dry-runs without side effects.

04

XSS-Hardened URLs

Blocks javascript:, vbscript:, file: protocols on images and links across four layers: parseHTML, renderHTML, commands, and input rules.

05

A11y Built In

ARIA roles on every node, full keyboard navigation, screen reader tested. Tab through the toolbar, Enter to activate, Esc to close menus. WCAG-friendly by default.

06

Zero-Jitter Floating UI

Zero JS during scroll events. Floating elements use position: absolute with @floating-ui/dom - the CSS compositor handles the rest.

07

SSR Ready

Server-side rendering via linkedom. Generate HTML on the server without a browser. Works with Angular Universal, Astro, and any Node.js environment.

01

Schema Conflict Detection

Duplicate extension names throw a clear error at startup. No mysterious production bugs from silent overwrites.

02

Extension Composability

create() → configure() → extend() with this.parent?.() to call the base version. Override any hook while preserving parent behavior.

03

Fluent Command API

editor.chain().focus().toggleBold().run() chains commands on a shared transaction. editor.can().toggleBold() dry-runs without side effects.

04

XSS-Hardened URLs

Blocks javascript:, vbscript:, file: protocols on images and links across four layers: parseHTML, renderHTML, commands, and input rules.

05

A11y Built In

ARIA roles on every node, full keyboard navigation, screen reader tested. Tab through the toolbar, Enter to activate, Esc to close menus. WCAG-friendly by default.

06

Zero-Jitter Floating UI

Zero JS during scroll events. Floating elements use position: absolute with @floating-ui/dom - the CSS compositor handles the rest.

07

SSR Ready

Server-side rendering via linkedom. Generate HTML on the server without a browser. Works with Angular Universal, Astro, and any Node.js environment.

01

Schema Conflict Detection

Duplicate extension names throw a clear error at startup. No mysterious production bugs from silent overwrites.

02

Extension Composability

create() → configure() → extend() with this.parent?.() to call the base version. Override any hook while preserving parent behavior.

03

Fluent Command API

editor.chain().focus().toggleBold().run() chains commands on a shared transaction. editor.can().toggleBold() dry-runs without side effects.

04

XSS-Hardened URLs

Blocks javascript:, vbscript:, file: protocols on images and links across four layers: parseHTML, renderHTML, commands, and input rules.

05

A11y Built In

ARIA roles on every node, full keyboard navigation, screen reader tested. Tab through the toolbar, Enter to activate, Esc to close menus. WCAG-friendly by default.

06

Zero-Jitter Floating UI

Zero JS during scroll events. Floating elements use position: absolute with @floating-ui/dom - the CSS compositor handles the rest.

07

SSR Ready

Server-side rendering via linkedom. Generate HTML on the server without a browser. Works with Angular Universal, Astro, and any Node.js environment.

01

Schema Conflict Detection

Duplicate extension names throw a clear error at startup. No mysterious production bugs from silent overwrites.

02

Extension Composability

create() → configure() → extend() with this.parent?.() to call the base version. Override any hook while preserving parent behavior.

03

Fluent Command API

editor.chain().focus().toggleBold().run() chains commands on a shared transaction. editor.can().toggleBold() dry-runs without side effects.

04

XSS-Hardened URLs

Blocks javascript:, vbscript:, file: protocols on images and links across four layers: parseHTML, renderHTML, commands, and input rules.

05

A11y Built In

ARIA roles on every node, full keyboard navigation, screen reader tested. Tab through the toolbar, Enter to activate, Esc to close menus. WCAG-friendly by default.

06

Zero-Jitter Floating UI

Zero JS during scroll events. Floating elements use position: absolute with @floating-ui/dom - the CSS compositor handles the rest.

07

SSR Ready

Server-side rendering via linkedom. Generate HTML on the server without a browser. Works with Angular Universal, Astro, and any Node.js environment.

01

Schema Conflict Detection

Duplicate extension names throw a clear error at startup. No mysterious production bugs from silent overwrites.

02

Extension Composability

create() → configure() → extend() with this.parent?.() to call the base version. Override any hook while preserving parent behavior.

03

Fluent Command API

editor.chain().focus().toggleBold().run() chains commands on a shared transaction. editor.can().toggleBold() dry-runs without side effects.

04

XSS-Hardened URLs

Blocks javascript:, vbscript:, file: protocols on images and links across four layers: parseHTML, renderHTML, commands, and input rules.

01

Schema Conflict Detection

Duplicate extension names throw a clear error at startup. No mysterious production bugs from silent overwrites.

02

Extension Composability

create() → configure() → extend() with this.parent?.() to call the base version. Override any hook while preserving parent behavior.

03

Fluent Command API

editor.chain().focus().toggleBold().run() chains commands on a shared transaction. editor.can().toggleBold() dry-runs without side effects.

04

XSS-Hardened URLs

Blocks javascript:, vbscript:, file: protocols on images and links across four layers: parseHTML, renderHTML, commands, and input rules.

01

Schema Conflict Detection

Duplicate extension names throw a clear error at startup. No mysterious production bugs from silent overwrites.

02

Extension Composability

create() → configure() → extend() with this.parent?.() to call the base version. Override any hook while preserving parent behavior.

03

Fluent Command API

editor.chain().focus().toggleBold().run() chains commands on a shared transaction. editor.can().toggleBold() dry-runs without side effects.

04

XSS-Hardened URLs

Blocks javascript:, vbscript:, file: protocols on images and links across four layers: parseHTML, renderHTML, commands, and input rules.

01

Schema Conflict Detection

Duplicate extension names throw a clear error at startup. No mysterious production bugs from silent overwrites.

02

Extension Composability

create() → configure() → extend() with this.parent?.() to call the base version. Override any hook while preserving parent behavior.

03

Fluent Command API

editor.chain().focus().toggleBold().run() chains commands on a shared transaction. editor.can().toggleBold() dry-runs without side effects.

04

XSS-Hardened URLs

Blocks javascript:, vbscript:, file: protocols on images and links across four layers: parseHTML, renderHTML, commands, and input rules.

005

A11y Built In

ARIA roles on every node, full keyboard navigation, screen reader tested. Tab through the toolbar, Enter to activate, Esc to close menus. WCAG-friendly by default.

006

Zero-Jitter Floating UI

Zero JS during scroll events. Floating elements use position: absolute with @floating-ui/dom - the CSS compositor handles the rest.

007

SSR Ready

Server-side rendering via linkedom. Generate HTML on the server without a browser. Works with Angular Universal, Astro, and any Node.js environment.

005

A11y Built In

ARIA roles on every node, full keyboard navigation, screen reader tested. Tab through the toolbar, Enter to activate, Esc to close menus. WCAG-friendly by default.

006

Zero-Jitter Floating UI

Zero JS during scroll events. Floating elements use position: absolute with @floating-ui/dom - the CSS compositor handles the rest.

007

SSR Ready

Server-side rendering via linkedom. Generate HTML on the server without a browser. Works with Angular Universal, Astro, and any Node.js environment.

005

A11y Built In

ARIA roles on every node, full keyboard navigation, screen reader tested. Tab through the toolbar, Enter to activate, Esc to close menus. WCAG-friendly by default.

006

Zero-Jitter Floating UI

Zero JS during scroll events. Floating elements use position: absolute with @floating-ui/dom - the CSS compositor handles the rest.

007

SSR Ready

Server-side rendering via linkedom. Generate HTML on the server without a browser. Works with Angular Universal, Astro, and any Node.js environment.

005

A11y Built In

ARIA roles on every node, full keyboard navigation, screen reader tested. Tab through the toolbar, Enter to activate, Esc to close menus. WCAG-friendly by default.

006

Zero-Jitter Floating UI

Zero JS during scroll events. Floating elements use position: absolute with @floating-ui/dom - the CSS compositor handles the rest.

007

SSR Ready

Server-side rendering via linkedom. Generate HTML on the server without a browser. Works with Angular Universal, Astro, and any Node.js environment.

WRAPPERS

Framework-agnostic core,
ready-made components.

The headless core works with any framework. Angular, React, Vue, and Vanilla wrappers ship today.

QUICK START

Get started
in minutes.

A few lines of code is all you need. Pick your framework.

$ npm install @domternal/core @domternal/theme @domternal/vanilla
import { StarterKit, BubbleMenu } from '@domternal/core';
import {
  DomternalEditor,
  DomternalToolbar,
  DomternalBubbleMenu,
} from '@domternal/vanilla';
import '@domternal/theme';

const editorEl = document.getElementById('editor')!;
const toolbarEl = document.getElementById('toolbar')!;
const bubbleEl = document.getElementById('bubble')!;

const dm = new DomternalEditor(editorEl, {
  extensions: [StarterKit, BubbleMenu.configure({ element: bubbleEl })],
  content: '<p>Hello from Vanilla!</p>',
});

new DomternalToolbar(toolbarEl, { editor: dm.editor });
new DomternalBubbleMenu(bubbleEl, { editor: dm.editor });
import {
  Editor, Document, Text, Paragraph,
  Bold, Italic, Underline,
} from '@domternal/core';

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>',
});
import { Component, signal } from '@angular/core';
import {
  DomternalEditorComponent,
  DomternalToolbarComponent,
  DomternalBubbleMenuComponent,
} from '@domternal/angular';
import { Editor, StarterKit, BubbleMenu } from '@domternal/core';

@Component({
  selector: 'app-editor',
  imports: [DomternalEditorComponent, DomternalToolbarComponent, DomternalBubbleMenuComponent],
  template: `
    @if (editor(); as ed) {
      <domternal-toolbar [editor]="ed" />
    }
    <domternal-editor
      [extensions]="extensions"
      [content]="content"
      (editorCreated)="editor.set($event)"
    />
    @if (editor(); as ed) {
      <domternal-bubble-menu [editor]="ed" />
    }
  `
})
export class EditorComponent {
  editor = signal<Editor | null>(null);
  extensions = [StarterKit, BubbleMenu];
  content = '<p>Hello from Angular!</p>';
}
import { Domternal } from '@domternal/react';
import { StarterKit, BubbleMenu } from '@domternal/core';

export default function Editor() {
  return (
    <Domternal
      extensions={[StarterKit, BubbleMenu]}
      content="<p>Hello from React!</p>"
    >
      <Domternal.Toolbar />
      <Domternal.Content />
      <Domternal.BubbleMenu />
    </Domternal>
  );
}
<script setup>
import { Domternal } from '@domternal/vue';
import { StarterKit, BubbleMenu } from '@domternal/core';

const extensions = [StarterKit, BubbleMenu];
</script>

<template>
  <Domternal
    :extensions="extensions"
    content="<p>Hello from Vue!</p>"
  >
    <Domternal.Toolbar />
    <Domternal.Content />
    <Domternal.BubbleMenu />
  </Domternal>
</template>
PRICING

Free forever.
No asterisk.

Open Source
Free forever
Complete editor engine
Angular, React, Vue & Vanilla components (editor, toolbar, menus)
Notion-style block menu, slash command & Table of Contents
Full tables: merge, resize, styling
65+ extensions across 15 packages
Built-in toolbar & theme (light + dark)
Image upload, emoji picker & mentions
Custom node views (React & Vue)
Get Started →
COMING SOON
Pro
Pricing TBA

Everything in Open Source, plus:

Real-time collaboration (Yjs/CRDT)
Inline comments & threads
Track changes & revision history
PDF / Word / Markdown export
AI writing assistant
Priority support & SLA
··· And more planned

Start building.
Ship something durable.

Open source. MIT licensed. Free forever.

Get notified when Pro launches

Leave your email and we'll let you know.