Getting Started
Choose Your Editor Style
Section titled “Choose Your Editor Style”One engine covers three kinds of editors; the difference is only which extensions and UI components you add:
- Classic editor - a toolbar plus a bubble menu, the familiar document-editing setup. Select the Classic tab below and pick your framework.
- Notion-style block editor - block handles, a slash menu, drag and drop, and nested blocks. Not a separate setup: it is the classic editor plus the extensions from
@domternal/extension-block-controls. Open the Notion tab below for the quickstart, go deeper with the Notion Mode guide, or build one step by step in the React tutorial. - Headless - just the engine, with your own UI on top. Open the Headless tab below and continue with the Editor API.
The chrome around the editable area is optional and comes apart piece by piece: the toolbar, the bubble menu, and the block-insert floating menu are separate components. The quickstarts below wire up the first two; add or drop any of them independently, the editor itself does not depend on them.
Pick your framework: Vanilla for a themed editor with toolbar and bubble menu out of the box, or Angular, React, or Vue for ready-made components with auto-rendered toolbar and menus.
-
Installation
Section titled “Installation”Terminal window pnpm add @domternal/core @domternal/theme @domternal/vanillaTerminal window npm install @domternal/core @domternal/theme @domternal/vanillaTerminal window yarn add @domternal/core @domternal/theme @domternal/vanilla -
Create Your First Editor
Section titled “Create Your First Editor”import { StarterKit, BubbleMenu } from '@domternal/core';import {DomternalEditor,DomternalToolbar,DomternalBubbleMenu,} from '@domternal/vanilla';import '@domternal/theme';const toolbarEl = document.getElementById('toolbar')!;const editorEl = document.getElementById('editor')!;const bubbleEl = document.getElementById('bubble')!;const dm = new DomternalEditor(editorEl, {extensions: [StarterKit, BubbleMenu.configure({ element: bubbleEl })],content: '<p>Hello world</p>',});new DomternalToolbar(toolbarEl, { editor: dm.editor });new DomternalBubbleMenu(bubbleEl, { editor: dm.editor });<div id="toolbar"></div><div id="editor" class="dm-editor"></div><div id="bubble" class="dm-bubble-menu"></div>StarterKitprovides paragraphs, headings, lists, blockquotes, code blocks, task lists, inline formatting, keyboard shortcuts, and undo/redo. The@domternal/vanillawrapper provides class-based components with.destroy()cleanup and EventTarget events. The@domternal/themepackage adds editor styles, a toolbar layout, and 51 built-in icons viadefaultIcons.For full control, skip
StarterKitand import only the extensions you need.
StarterKit Contents
Section titled “StarterKit Contents”Every extension in the kit can be disabled with false or configured with options:
StarterKit.configure({ codeBlock: false, // disable an extension heading: { levels: [1, 2, 3, 4] }, // limit heading levels history: { depth: 50 }, // configure undo stack link: { openOnClick: false }, // keep links non-clickable while editing linkPopover: false, // disable the built-in link popover})The full list of bundled extensions:
| Category | Included |
|---|---|
| Nodes | Document, Text, Paragraph, Heading, Blockquote, CodeBlock, BulletList, OrderedList, ListItem, TaskList, TaskItem, HorizontalRule, HardBreak |
| Marks | Bold, Italic, Underline, Strike, Code, Link |
| Behaviors | BaseKeymap, History, Dropcursor, Gapcursor, TrailingNode, ListKeymap, LinkPopover, SelectionDecoration |
Next Steps
Section titled “Next Steps”Now that your editor is running, explore the rest of the toolkit:
- StarterKit - see above for the full list of bundled extensions and how to configure them
- Extensions - add tables, images, emoji, mentions, and syntax-highlighted code blocks via standalone packages
- Notion Mode - turn it into a Notion-style block editor with block handles, a slash menu, and drag and drop
- Theming - customize colors, spacing, and fonts with 160+ CSS custom properties on
.dm-editor - Editor API - chain commands, listen to events, and read or update content programmatically
- StackBlitz Example - a full working vanilla editor with all extensions you can edit in the browser
-
Installation
Section titled “Installation”Terminal window pnpm add @domternal/core @domternal/theme @domternal/angularTerminal window npm install @domternal/core @domternal/theme @domternal/angularTerminal window yarn add @domternal/core @domternal/theme @domternal/angular -
Create Your First Editor
Section titled “Create Your First Editor”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],templateUrl: './editor.html',})export class EditorComponent {editor = signal<Editor | null>(null);extensions = [StarterKit, BubbleMenu];content = '<p>Hello from Angular!</p>';}@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" />} -
Add the Theme
Section titled “Add the Theme”Add the theme to your global stylesheet to load editor and toolbar styles:
styles.scss @use '@domternal/theme';The toolbar and bubble menu components auto-render buttons based on the extensions you provide. No manual button wiring needed.
What You Get Out of the Box
Section titled “What You Get Out of the Box”The Angular components handle everything automatically:
<domternal-toolbar>renders buttons based on the extensions you provide, with active and disabled states<domternal-bubble-menu>appears on text selection with contextual formatting options- Signals drive reactivity, no manual subscription management needed
- Both toolbar and bubble menu support custom layouts so you can choose which buttons to show and in what order
StarterKit Contents
Section titled “StarterKit Contents”Every extension in the kit can be disabled with false or configured with options:
StarterKit.configure({ codeBlock: false, // disable an extension heading: { levels: [1, 2, 3, 4] }, // limit heading levels history: { depth: 50 }, // configure undo stack link: { openOnClick: false }, // keep links non-clickable while editing linkPopover: false, // disable the built-in link popover})The full list of bundled extensions:
| Category | Included |
|---|---|
| Nodes | Document, Text, Paragraph, Heading, Blockquote, CodeBlock, BulletList, OrderedList, ListItem, TaskList, TaskItem, HorizontalRule, HardBreak |
| Marks | Bold, Italic, Underline, Strike, Code, Link |
| Behaviors | BaseKeymap, History, Dropcursor, Gapcursor, TrailingNode, ListKeymap, LinkPopover, SelectionDecoration |
Next Steps
Section titled “Next Steps”Now that your editor is running, explore the rest of the toolkit:
- StarterKit - see above for the full list of bundled extensions and how to configure them
- Extensions - add tables, images, emoji, mentions, and syntax-highlighted code blocks via standalone packages
- Notion Mode - turn it into a Notion-style block editor with block handles, a slash menu, and drag and drop
- Theming - customize the look with 160+ CSS custom properties
- Reactive Forms - bind editor content with
formControlNameorngModel - Editor API - chain commands, listen to events, and read or update content programmatically
- StackBlitz Example - a full working Angular editor with all extensions you can edit in the browser
-
Installation
Section titled “Installation”Terminal window pnpm add @domternal/core @domternal/theme @domternal/reactTerminal window npm install @domternal/core @domternal/theme @domternal/reactTerminal window yarn add @domternal/core @domternal/theme @domternal/react -
Create Your First Editor
Section titled “Create Your First Editor”import { Domternal } from '@domternal/react';import { StarterKit, BubbleMenu } from '@domternal/core';export default function Editor() {return (<Domternalextensions={[StarterKit, BubbleMenu]}content="<p>Hello from React!</p>"><Domternal.Toolbar /><Domternal.Content /><Domternal.BubbleMenu /></Domternal>);}Domternalwraps your editor with a context provider. The subcomponents (Toolbar,Content,BubbleMenu) automatically connect to the editor instance. No manual wiring needed. -
Add the Theme
Section titled “Add the Theme”Import the theme in your root CSS or entry file to load editor and toolbar styles:
index.css @import '@domternal/theme';The toolbar and bubble menu components auto-render buttons based on the extensions you provide. No manual button wiring needed.
What You Get Out of the Box
Section titled “What You Get Out of the Box”The React components handle everything automatically:
<Domternal.Toolbar />renders buttons based on the extensions you provide, with active and disabled states<Domternal.BubbleMenu />appears on text selection with contextual formatting optionsuseEditorhook for lower-level control when you need custom behavioruseEditorStatehook with selector support for granular re-renders- Both toolbar and bubble menu support custom layouts so you can choose which buttons to show and in what order
StarterKit Contents
Section titled “StarterKit Contents”Every extension in the kit can be disabled with false or configured with options:
StarterKit.configure({ codeBlock: false, // disable an extension heading: { levels: [1, 2, 3, 4] }, // limit heading levels history: { depth: 50 }, // configure undo stack link: { openOnClick: false }, // keep links non-clickable while editing linkPopover: false, // disable the built-in link popover})The full list of bundled extensions:
| Category | Included |
|---|---|
| Nodes | Document, Text, Paragraph, Heading, Blockquote, CodeBlock, BulletList, OrderedList, ListItem, TaskList, TaskItem, HorizontalRule, HardBreak |
| Marks | Bold, Italic, Underline, Strike, Code, Link |
| Behaviors | BaseKeymap, History, Dropcursor, Gapcursor, TrailingNode, ListKeymap, LinkPopover, SelectionDecoration |
Next Steps
Section titled “Next Steps”Now that your editor is running, explore the rest of the toolkit:
- StarterKit - see above for the full list of bundled extensions and how to configure them
- Extensions - add tables, images, emoji, mentions, and syntax-highlighted code blocks via standalone packages
- Notion Mode - turn it into a Notion-style block editor, or follow the step-by-step tutorial
- Theming - customize the look with 160+ CSS custom properties
- Hooks - use
useEditoranduseEditorStatefor full control over editor state - Editor API - chain commands, listen to events, and read or update content programmatically
- StackBlitz Example - a full working React editor with all extensions you can edit in the browser
-
Installation
Section titled “Installation”Terminal window pnpm add @domternal/core @domternal/theme @domternal/vueTerminal window npm install @domternal/core @domternal/theme @domternal/vueTerminal window yarn add @domternal/core @domternal/theme @domternal/vue -
Create Your First Editor
Section titled “Create Your First Editor”<script setup lang="ts">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>Domternalwraps your editor with a provide/inject context. The subcomponents (Toolbar,Content,BubbleMenu) automatically connect to the editor instance. No manual wiring needed. -
Add the Theme
Section titled “Add the Theme”Import the theme in your main entry file or component to load editor and toolbar styles:
main.ts import '@domternal/theme';The toolbar and bubble menu components auto-render buttons based on the extensions you provide. No manual button wiring needed.
What You Get Out of the Box
Section titled “What You Get Out of the Box”The Vue components handle everything automatically:
<Domternal.Toolbar />renders buttons based on the extensions you provide, with active and disabled states<Domternal.BubbleMenu />appears on text selection with contextual formatting optionsuseEditorcomposable for lower-level control when you need custom behavioruseEditorStatecomposable with selector support for granular reactivity- Both toolbar and bubble menu support custom layouts so you can choose which buttons to show and in what order
StarterKit Contents
Section titled “StarterKit Contents”Every extension in the kit can be disabled with false or configured with options:
StarterKit.configure({ codeBlock: false, // disable an extension heading: { levels: [1, 2, 3, 4] }, // limit heading levels history: { depth: 50 }, // configure undo stack link: { openOnClick: false }, // keep links non-clickable while editing linkPopover: false, // disable the built-in link popover})The full list of bundled extensions:
| Category | Included |
|---|---|
| Nodes | Document, Text, Paragraph, Heading, Blockquote, CodeBlock, BulletList, OrderedList, ListItem, TaskList, TaskItem, HorizontalRule, HardBreak |
| Marks | Bold, Italic, Underline, Strike, Code, Link |
| Behaviors | BaseKeymap, History, Dropcursor, Gapcursor, TrailingNode, ListKeymap, LinkPopover, SelectionDecoration |
Next Steps
Section titled “Next Steps”Now that your editor is running, explore the rest of the toolkit:
- StarterKit - see above for the full list of bundled extensions and how to configure them
- Extensions - add tables, images, emoji, mentions, and syntax-highlighted code blocks via standalone packages
- Notion Mode - turn it into a Notion-style block editor with block handles, a slash menu, and drag and drop
- Theming - customize the look with 160+ CSS custom properties
- Composables - use
useEditoranduseEditorStatefor full control over editor state - Editor API - chain commands, listen to events, and read or update content programmatically
- StackBlitz Example - a full working Vue editor with all extensions you can edit in the browser
The same extension list powers every framework; only the mounting differs. Pick yours:
-
Installation
Section titled “Installation”Terminal window pnpm add @domternal/core @domternal/theme @domternal/extension-block-controlsTerminal window npm install @domternal/core @domternal/theme @domternal/extension-block-controlsTerminal window yarn add @domternal/core @domternal/theme @domternal/extension-block-controlsNo wrapper package needed for this minimal setup: the block UI (handle, slash menu, context menu) is rendered by the extensions themselves.
-
Create Your First Block Editor
Section titled “Create Your First Block Editor”import {Editor, StarterKit, UniqueID, Placeholder,BlockColor, ListIndent,} from '@domternal/core';import {BlockHandle, BlockContextMenu, SlashCommand,SmartPaste, KeyboardReorder,} from '@domternal/extension-block-controls';import '@domternal/theme';const editor = new Editor({element: document.getElementById('editor')!,extensions: [StarterKit,UniqueID,Placeholder.configure({ placeholder: "Press '/' for commands" }),BlockColor,ListIndent,BlockHandle.configure({ nested: true }),BlockContextMenu,SlashCommand,SmartPaste,KeyboardReorder,],content: '<h1>Project kickoff</h1><p>Type / to insert your first block.</p>',});<div id="editor" class="dm-editor dm-notion-mode"></div>
-
Installation
Section titled “Installation”Terminal window pnpm add @domternal/core @domternal/theme @domternal/extension-block-controls @domternal/angularTerminal window npm install @domternal/core @domternal/theme @domternal/extension-block-controls @domternal/angularTerminal window yarn add @domternal/core @domternal/theme @domternal/extension-block-controls @domternal/angular -
Create Your First Block Editor
Section titled “Create Your First Block Editor”import { Component } from '@angular/core';import { DomternalEditorComponent } from '@domternal/angular';import {StarterKit, UniqueID, Placeholder,BlockColor, ListIndent,} from '@domternal/core';import {BlockHandle, BlockContextMenu, SlashCommand,SmartPaste, KeyboardReorder,} from '@domternal/extension-block-controls';@Component({selector: 'app-notion-editor',imports: [DomternalEditorComponent],templateUrl: './notion-editor.html',})export class NotionEditorComponent {extensions = [StarterKit,UniqueID,Placeholder.configure({ placeholder: "Press '/' for commands" }),BlockColor,ListIndent,BlockHandle.configure({ nested: true }),BlockContextMenu,SlashCommand,SmartPaste,KeyboardReorder,];content = '<h1>Project kickoff</h1><p>Type / to insert your first block.</p>';}<domternal-editorclass="dm-notion-mode"[extensions]="extensions"[content]="content"/>Add
@use '@domternal/theme';to your global stylesheet, the same as in the classic quickstart.
-
Installation
Section titled “Installation”Terminal window pnpm add @domternal/core @domternal/theme @domternal/extension-block-controls @domternal/reactTerminal window npm install @domternal/core @domternal/theme @domternal/extension-block-controls @domternal/reactTerminal window yarn add @domternal/core @domternal/theme @domternal/extension-block-controls @domternal/react -
Create Your First Block Editor
Section titled “Create Your First Block Editor”import { Domternal } from '@domternal/react';import {StarterKit, UniqueID, Placeholder,BlockColor, ListIndent,} from '@domternal/core';import {BlockHandle, BlockContextMenu, SlashCommand,SmartPaste, KeyboardReorder,} from '@domternal/extension-block-controls';import '@domternal/theme';const extensions = [StarterKit,UniqueID,Placeholder.configure({ placeholder: "Press '/' for commands" }),BlockColor,ListIndent,BlockHandle.configure({ nested: true }),BlockContextMenu,SlashCommand,SmartPaste,KeyboardReorder,];export default function NotionEditor() {return (<Domternalextensions={extensions}content="<h1>Project kickoff</h1><p>Type / to insert your first block.</p>"><Domternal.Content className="dm-notion-mode" /></Domternal>);}
-
Installation
Section titled “Installation”Terminal window pnpm add @domternal/core @domternal/theme @domternal/extension-block-controls @domternal/vueTerminal window npm install @domternal/core @domternal/theme @domternal/extension-block-controls @domternal/vueTerminal window yarn add @domternal/core @domternal/theme @domternal/extension-block-controls @domternal/vue -
Create Your First Block Editor
Section titled “Create Your First Block Editor”<script setup lang="ts">import { Domternal } from '@domternal/vue';import {StarterKit, UniqueID, Placeholder,BlockColor, ListIndent,} from '@domternal/core';import {BlockHandle, BlockContextMenu, SlashCommand,SmartPaste, KeyboardReorder,} from '@domternal/extension-block-controls';import '@domternal/theme';const extensions = [StarterKit,UniqueID,Placeholder.configure({ placeholder: "Press '/' for commands" }),BlockColor,ListIndent,BlockHandle.configure({ nested: true }),BlockContextMenu,SlashCommand,SmartPaste,KeyboardReorder,];</script><template><Domternal:extensions="extensions"content="<h1>Project kickoff</h1><p>Type / to insert your first block.</p>"><Domternal.Content class="dm-notion-mode" /></Domternal></template>
Every tab mounts the same extension list. The demo above is the homepage Notion preset: this setup plus the wrapper bubble menu, color picker, and floating menu components, and a few more block extensions for the slash menu. The dm-notion-mode class switches the theme to the Notion layout: centered content, a side gutter for the block handle, and a larger base font.
What You Get Out of the Box
Section titled “What You Get Out of the Box”- Block handle appears in the gutter on hover: drag to reorder, drag right while dropping to nest, click
+to insert a block below - Slash command - type
/to open the insert menu with all available block types - Block context menu - click the handle for Delete, Duplicate, Turn into, Colors, and Copy link
- Keyboard reorder -
Mod-Shift-ArrowUp/Downmoves the current block - Smart paste keeps block formats intact when pasting between blocks
Next Steps
Section titled “Next Steps”- Notion Mode guide - the complete setup with floating TOC, highlighted code blocks, and the color picker
- React tutorial - build a full Notion-style editor step by step, with videos
- Theming - customize the look with 160+ CSS custom properties
- Editor API - chain commands, listen to events, and read or update content programmatically
Select text and press Mod+B for bold, Mod+I for italic, or Mod+U for underline.
-
Installation
Section titled “Installation”Terminal window pnpm add @domternal/coreTerminal window npm install @domternal/coreTerminal window yarn add @domternal/core -
Create Your First Editor
Section titled “Create Your First Editor”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>',});<div id="editor"></div>Import only what you need for full control and zero bloat. Use
StarterKitinstead for a batteries-included setup with headings, lists, code blocks, history, and more.
Next Steps
Section titled “Next Steps”Now that your editor is running, explore the rest of the toolkit:
- StarterKit - use
StarterKitfor a batteries-included setup with headings, lists, code blocks, history, and more - Extensions - add tables, images, emoji, mentions, and syntax-highlighted code blocks via standalone packages
- Notion Mode - turn it into a Notion-style block editor with block handles, a slash menu, and drag and drop
- Theming - style your editor with CSS or use the ready-made
@domternal/theme - Editor API - chain commands, listen to events, and read or update content programmatically