Selection Decoration
SelectionDecoration collapses range selections to a cursor when the editor loses focus. This prevents a “ghost selection” from lingering after the user clicks outside the editor, matching the behavior of Google Docs and Notion. Toolbar and bubble menu buttons use preventDefault() on mousedown, so they never trigger blur and the selection stays intact during editor UI interactions.
Included in StarterKit. Disable with StarterKit.configure({ selectionDecoration: false }).
import { Editor, Document, Paragraph, Text, SelectionDecoration } from '@domternal/core';import '@domternal/theme';
const editor = new Editor({ element: document.getElementById('editor')!, extensions: [Document, Paragraph, Text, SelectionDecoration],});Select text, then click outside the editor. The selection collapses to a cursor instead of remaining highlighted.
import { Component, signal } from '@angular/core';import { DomternalEditorComponent } from '@domternal/angular';import { Editor, Document, Paragraph, Text, SelectionDecoration } from '@domternal/core';
@Component({ selector: 'app-editor', imports: [DomternalEditorComponent], templateUrl: './editor.html',})export class EditorComponent { editor = signal<Editor | null>(null); extensions = [Document, Paragraph, Text, SelectionDecoration];}<domternal-editor [extensions]="extensions" (editorCreated)="editor.set($event)"/>import { Domternal } from '@domternal/react';import { Document, Paragraph, Text, SelectionDecoration } from '@domternal/core';
export default function Editor() { return ( <Domternal extensions={[Document, Paragraph, Text, SelectionDecoration]} > <Domternal.Content /> </Domternal> );}Select text, then click outside the editor. The selection collapses to a cursor instead of remaining highlighted.
import { Editor, Document, Paragraph, Text, SelectionDecoration } from '@domternal/core';
const editor = new Editor({ element: document.getElementById('editor')!, extensions: [Document, Paragraph, Text, SelectionDecoration],});SelectionDecoration has no options, commands, or visual output. It only affects blur behavior.
Options
Section titled “Options”SelectionDecoration has no configurable options.
Commands
Section titled “Commands”SelectionDecoration does not register any commands.
Keyboard shortcuts
Section titled “Keyboard shortcuts”SelectionDecoration does not register any keyboard shortcuts.
Input rules
Section titled “Input rules”SelectionDecoration does not register any input rules.
Toolbar items
Section titled “Toolbar items”SelectionDecoration does not register any toolbar items.
How it works
Section titled “How it works”Blur handler
Section titled “Blur handler”SelectionDecoration creates a ProseMirror plugin with a handleDOMEvents.blur handler. When the editor’s contenteditable element loses focus:
- Checks if focus moved to an editor-related UI element (marked with
[data-dm-editor-ui]). If so, does nothing. - Checks if the current selection is a range (not collapsed). If
from !== to, dispatches a transaction that collapses the selection to thefromposition.
blur(view, event) { const related = event.relatedTarget; if (related instanceof HTMLElement && related.closest('[data-dm-editor-ui]')) { return false; // focus moved to editor UI, keep selection }
const { from, to } = view.state.selection; if (from !== to) { view.dispatch( view.state.tr.setSelection(TextSelection.create(view.state.doc, from)) ); } return false;}Editor UI exclusion
Section titled “Editor UI exclusion”Elements marked with the data-dm-editor-ui attribute are treated as part of the editor. When focus moves to these elements (e.g., a link popover input field), the selection is preserved. This prevents the selection from collapsing when the user interacts with:
- Link popover URL inputs
- Image popover fields
- Any custom UI element with
data-dm-editor-ui
Toolbar compatibility
Section titled “Toolbar compatibility”Toolbar and bubble menu buttons call event.preventDefault() on mousedown. This prevents the browser from moving focus away from the editor, so the blur event never fires. The selection stays intact while clicking toolbar buttons, making SelectionDecoration transparent to toolbar interactions.
Collapsed selections
Section titled “Collapsed selections”If the selection is already collapsed (cursor, from === to), the blur handler does nothing. Only range selections are affected.
Exports
Section titled “Exports”import { SelectionDecoration, selectionDecorationPluginKey } from '@domternal/core';import type { SelectionDecorationOptions } from '@domternal/core';| Export | Type | Description |
|---|---|---|
SelectionDecoration | Extension | The selection decoration extension |
selectionDecorationPluginKey | PluginKey | The ProseMirror plugin key |
SelectionDecorationOptions | TypeScript type | Options for SelectionDecoration.configure() (empty) |
Source
Section titled “Source”@domternal/core - SelectionDecoration.ts