Plate
PlateEditorsTemplates
GitHub16kGitHub
DiscordDiscord
  • Plate
  • Editor API
  • Editor Transforms
  • Node
  • Element
  • Text
  • Path
  • Point
  • Range
  • Location
  • Anchor
  • Selection
  • Document Change
  • DOM API
  • React Hooks
  • Plate Core
    • Plate Components
    • Plate Editor
    • Plate Plugin
    • Editor Context
    • Plate Controller
  • Plate Utils
  • Resizable

DOM API

PreviousNext

Read and update the mounted editor view, selection, focus, and clipboard.

createEditor creates an editor with React, DOM, and clipboard host APIs installed. Use useCreateEditor to retain an editor in a React component.

Minimal usage

import { createEditor } from "platejs/react";
 
const editor = createEditor();
 
editor.api.dom.focus();




SelectionReact Hooks

On This Page

Minimal usageCheckseditor.api.react.isComposing(): booleaneditor.api.react.isFocused(): booleaneditor.api.react.isReadOnly(): booleaneditor.api.dom.isComposing(): booleaneditor.api.dom.isFocused(): booleaneditor.api.dom.isReadOnly(): booleanFocus and selectioneditor.api.dom.blur(): voideditor.api.dom.focus(options?: { retries?: number }): voideditor.api.dom.deselect(): voidTyped texteditor.api.react.subscribeTypedText(listener: (typed: TypedText) => void): () => voidDOM scopeeditor.api.dom.root(): HTMLElement | nulleditor.api.dom.editable(root?: RootKey): HTMLElement | nulleditor.api.dom.scroll(): HTMLElement | nullDOM translationeditor.api.dom.findKey(node: Descendant): Keyeditor.api.dom.resolvePath(node: Node): Path | nulleditor.api.dom.assertPath(node: Node): Patheditor.api.dom.hasDOMNode(target: DOMNode, options?: { editable?: boolean }): booleaneditor.api.dom.hasEditableTarget(target: EventTarget | null): target is DOMNodeeditor.api.dom.hasSelectableTarget(target: EventTarget | null): booleaneditor.api.dom.hasTarget(target: EventTarget | null): target is DOMNodeeditor.api.dom.assertDOMNode(node: Node): HTMLElementeditor.api.dom.resolveDOMNode(nodeOrKey: Node | NodeKey): HTMLElement | nulleditor.api.dom.assertDOMPoint(point: Point): DOMPointeditor.api.dom.resolveDOMPoint(point: Point): DOMPoint | nulleditor.api.dom.assertDOMRange(range: Range): DOMRangeeditor.api.dom.resolveDOMRange(range: Range): DOMRange | nulleditor.api.dom.resolveRangeRect(range: Range): DOMRect | nulleditor.api.dom.textToCaret(from: Point): string | nulleditor.api.dom.resolveVisualPoint(point: Point, options: { affinity?: SelectionAssociation; direction: 'left' | 'right'; unit: 'character' | 'word' }): DOMVisualPoint | nulleditor.api.dom.scrollIntoView(target: Path | Point | Range | DOMRange, options?: ScrollIntoViewOptions): () => voideditor.api.dom.assertEventRange(event: unknown): Rangeeditor.api.dom.resolveEventRange(event: unknown): Range | nulleditor.api.dom.assertNode(domNode: DOMNode): Nodeeditor.api.dom.resolveNode(domNode: DOMNode): Node | nulleditor.api.dom.assertPoint(domPoint: DOMPoint, options: { exactMatch: boolean; searchDirection?: 'backward' | 'forward' }): Pointeditor.api.dom.resolvePoint(domPoint: DOMPoint, options: { exactMatch: boolean; searchDirection?: 'backward' | 'forward' }): Point | nulleditor.api.dom.assertRange(domRange: DOMRange | DOMStaticRange | DOMSelection, options: { exactMatch: boolean }): Rangeeditor.api.dom.resolveRange(domRange: DOMRange | DOMStaticRange | DOMSelection, options: { exactMatch: boolean }): Range | nullDOM environmenteditor.api.dom.findDocumentOrShadowRoot(): Document | ShadowRooteditor.api.dom.getWindow(): Windoweditor.api.dom.hasRange(range: Range): booleaneditor.api.dom.isTargetInsideNonReadonlyVoid(target: EventTarget | null): booleanDrag and dropeditor.api.dom.drageditor.api.dom.drag.start(event: DragEvent, options: { node: Element }): { origin: { x: number; y: number }; previews: HTMLElement[] } | nulleditor.api.dom.drag.indicate(target: DropTarget | null): voideditor.api.dom.resolveDropTarget(input: { clientX: number; clientY: number; target?: EventTarget | null; dataTransfer?: DataTransfer | null }, options?: { copy?: boolean; files?: Element; from?: Editor; nodes?: NodeKey[]; range?: Range }): { key: NodeKey; edge: 'after' | 'before'; axis: 'x' | 'y' } | { key: NodeKey; side: 'end' | 'start' } | { point: Point } | nullDataTransfereditor.api.dom.clipboard.insertData(data: DataTransfer): booleaneditor.api.dom.clipboard.insertFragmentData(data: DataTransfer): booleaneditor.api.dom.clipboard.insertTextData(data: DataTransfer): booleaneditor.api.dom.clipboard.readSlice(data: Pick<DataTransfer, 'getData' | 'types'>): ClipboardSliceReadeditor.api.dom.clipboard.writeSelection(data: Pick<DataTransfer, 'getData' | 'setData'>): voideditor.api.dom.clipboard.writeSlice(data: Pick<DataTransfer, 'getData' | 'setData'>, payload: ClipboardSliceWrite): voidResolution failuresHost utilities
Build your editor
Production-ready AI template and reusable components.
Get all-access
import { createEditor } from "platejs/react";
const editor = createEditor();
editor.api.dom.focus();

Checks

editor.api.react.isComposing(): boolean

Check if the user is currently composing inside the editor.

editor.api.react.isFocused(): boolean

Check if the editor is focused.

editor.api.react.isReadOnly(): boolean

Check if the editor is in read-only mode.

editor.api.dom.isComposing(): boolean

Check if the user is currently composing inside the editor from the DOM bridge.

editor.api.dom.isFocused(): boolean

Check if the DOM editor is focused.

editor.api.dom.isReadOnly(): boolean

Check if the DOM editor is in read-only mode.

Focus and selection

editor.api.dom.blur(): void

Blur the editor.

editor.api.dom.focus(options?: { retries?: number }): void

Focus the editor.

editor.api.dom.deselect(): void

Clear the native DOM selection and the editor selection.

Typed text

editor.api.react.subscribeTypedText(listener: (typed: TypedText) => void): () => void

Call listener with the text each mounted editable types: an insertText input or IME composition commit whose one change is an insertion ending at a collapsed caret. typed.editable is the editable root element that received the input, typed.range the inserted text in the model, ending at the caret, and typed.text that text. Returns the unsubscribe function.

Paste, drop, yank, replacement text, history replay, remote edits and typing over a selection report nothing, and so does a commit whose document an earlier commit listener changes before the report. Android keyboards that paste through insertText report the pasted text. When an Android keyboard sends typed and pasted text for the same text node before one input event, that input reports nothing.

DOM scope

editor.api.dom.root(): HTMLElement | null

Return the mounted editor root element.

editor.api.dom.editable(root?: RootKey): HTMLElement | null

Return the mounted editable element for a document root.

editor.api.dom.scroll(): HTMLElement | null

Return the element used as the editor scroll container. It falls back to the editor root when no custom scroll element is registered.

DOM translation

editor.api.dom.findKey(node: Descendant): Key

Find a key for an editor node.

editor.api.dom.resolvePath(node: Node): Path | null

Resolve the current path of an editor node. Returns null when the node is not mounted in the current editor value.

editor.api.dom.assertPath(node: Node): Path

Assert the current path of an editor node.

editor.api.dom.hasDOMNode(target: DOMNode, options?: { editable?: boolean }): boolean

Check if a DOM node is within the editor.

editor.api.dom.hasEditableTarget(target: EventTarget | null): target is DOMNode

Check if the target is editable and in the editor.

editor.api.dom.hasSelectableTarget(target: EventTarget | null): boolean

Check if the target can be selected by the editor.

editor.api.dom.hasTarget(target: EventTarget | null): target is DOMNode

Check if the target is in the editor.

editor.api.dom.assertDOMNode(node: Node): HTMLElement

Assert the native DOM element for an editor node.

editor.api.dom.resolveDOMNode(nodeOrKey: Node | NodeKey): HTMLElement | null

Resolve the native DOM element for an editor node or its live NodeKey. Returns null when the node is not mounted or the key is removed or belongs to another editor.

editor.api.dom.assertDOMPoint(point: Point): DOMPoint

Assert a native DOM selection point from an editor point.

editor.api.dom.resolveDOMPoint(point: Point): DOMPoint | null

Resolve a native DOM selection point from an editor point. Returns null when the editor point is not currently mappable.

editor.api.dom.assertDOMRange(range: Range): DOMRange

Assert a native DOM range from an editor range.

editor.api.dom.resolveDOMRange(range: Range): DOMRange | null

Resolve a native DOM range from an editor range. Returns null when the editor range is not currently mappable.

editor.api.dom.resolveRangeRect(range: Range): DOMRect | null

Resolve the bounding rect for an editor range. Returns null when the range is not currently mappable to mounted DOM.

editor.api.dom.textToCaret(from: Point): string | null

Read the DOM text from from to the native caret, including IME preedit text the editor has not received yet. Non-editable, retained and nested-editor DOM is left out. Returns null when the editor root or from is not mounted, when from is in another root, or when the caret is outside this editor or before from.

editor.api.dom.resolveVisualPoint(point: Point, options: { affinity?: SelectionAssociation; direction: 'left' | 'right'; unit: 'character' | 'word' }): DOMVisualPoint | null

Resolve one physical horizontal caret step through the mounted browser layout. The result carries both the next model point and its affinity. Returns null when the point is not mounted or the browser cannot resolve the step.

editor.api.dom.scrollIntoView(target: Path | Point | Range | DOMRange, options?: ScrollIntoViewOptions): () => void

Schedule scrolling to an editor path, point, range, or native DOM range in the mounted view. Range targets use their bounding rectangle, so caret scrolling follows the actual position inside a text leaf. The returned function cancels pending work for this request; return it from an effect to cancel scrolling when its target changes or the component unmounts.

editor.api.dom.assertEventRange(event: unknown): Range

Assert the target range from a DOM event.

editor.api.dom.resolveEventRange(event: unknown): Range | null

Resolve the target range from a DOM event. Returns null when the event target cannot be mapped into the editor.

editor.api.dom.assertNode(domNode: DOMNode): Node

Assert an editor node from a native DOM node.

editor.api.dom.resolveNode(domNode: DOMNode): Node | null

Resolve an editor node from a native DOM node. Returns null when the DOM node is not owned by the editor.

editor.api.dom.assertPoint(domPoint: DOMPoint, options: { exactMatch: boolean; searchDirection?: 'backward' | 'forward' }): Point

Assert an editor point from a DOM point.

editor.api.dom.resolvePoint(domPoint: DOMPoint, options: { exactMatch: boolean; searchDirection?: 'backward' | 'forward' }): Point | null

Resolve an editor point from a DOM point. Returns null when the DOM point is not currently mappable.

editor.api.dom.assertRange(domRange: DOMRange | DOMStaticRange | DOMSelection, options: { exactMatch: boolean }): Range

Assert an editor range from a DOM range or selection.

editor.api.dom.resolveRange(domRange: DOMRange | DOMStaticRange | DOMSelection, options: { exactMatch: boolean }): Range | null

Resolve an editor range from a DOM range or selection. Returns null when the DOM range is not currently mappable.

DOM environment

editor.api.dom.findDocumentOrShadowRoot(): Document | ShadowRoot

Return the document or shadow root that owns the editor.

editor.api.dom.getWindow(): Window

Return the window that owns the editor.

editor.api.dom.hasRange(range: Range): boolean

Check whether an editor range can currently be mapped to DOM.

editor.api.dom.isTargetInsideNonReadonlyVoid(target: EventTarget | null): boolean

Check whether a DOM event target is inside a non-read-only void element.

Drag and drop

editor.api.dom.drag

Native block dragging for drag handles and custom drivers. A block drag moves inside one document and copies between editors; every drop runs editor.api.transfer.

editor.api.dom.drag.start(event: DragEvent, options: { node: Element }): { origin: { x: number; y: number }; previews: HTMLElement[] } | null

Start a block drag from a handle's dragstart. It selects the blocks editor.read.transfer.nodes({ node }) returns, such as the selected blocks around node or a list item's family, marks their hosts with data-editor-dragging, and returns inert previews plus the pointer's offset for event.dataTransfer.setDragImage. A read-only view starts a copy-only drag. Returns null when nothing can be dragged.

editor.api.dom.drag.indicate(target: DropTarget | null): void

Publish this view's drop indicator for a target from resolveDropTarget, or clear it. useDropIndicator reads it.

editor.api.dom.resolveDropTarget(input: { clientX: number; clientY: number; target?: EventTarget | null; dataTransfer?: DataTransfer | null }, options?: { copy?: boolean; files?: Element; from?: Editor; nodes?: NodeKey[]; range?: Range }): { key: NodeKey; edge: 'after' | 'before'; axis: 'x' | 'y' } | { key: NodeKey; side: 'end' | 'start' } | { point: Point } | null

Resolve where a drop under a pointer lands: an edge the schema, the landing redirects and the vetoes admit, or a text point for a text drag. Within 8px of a container's top or bottom edge, clamped to a quarter of its height, the drop lands beside that container when that edge is admitted; otherwise it lands beside the block under the pointer, or beside an outer block when the schema or a veto refuses that edge. An edge inside the dragged blocks, or one that would not move them, drops nothing. A move whose pointer is over or beside its own blocks also drops nothing, even within a container's band. Edges inside collapsed content are refused. Without options it reads the active drag session. Pass files with the block that dropped files become to place an upload. The drop checks one such block per file the browser exposes, and one block when the browser hides them during the drag.

When a feature builds side landings, such as the Column plugin, a move inside the editor whose pointer rests in the inline-end strip of the innermost block resolves to { key, side: 'end' }. The strip is the outer 24px of the block's box, at most a quarter of its width, in the middle half of its height; a container's band wins where they overlap. A copy, a file drop or a drag from another editor never resolves to a side. Pass the target to editor.api.transfer.move as to, or narrow with 'edge' in target.

DataTransfer

editor.api.dom.clipboard.insertData(data: DataTransfer): boolean

Insert data from a DataTransfer into the editor. Returns true when Plate or a plugin inserts content. The DOM adapter dispatches domCommands.insertData; typed plugin command interceptors can claim the payload, transform it, or return next() to continue. The accepted command spec commits through one transaction.

The default command path reads the editor fragment format, then configured DataTransfer formats, then plain text.

editor.api.dom.clipboard.insertFragmentData(data: DataTransfer): boolean

Insert editor fragment data from a DataTransfer. Returns true when fragment content was inserted.

editor.api.dom.clipboard.insertTextData(data: DataTransfer): boolean

Insert plain text data from a DataTransfer. Returns true when text content was inserted.

editor.api.dom.clipboard.readSlice(data: Pick<DataTransfer, 'getData' | 'types'>): ClipboardSliceRead

Read one exact editor slice. The result distinguishes absent data, invalid MIME or HTML data, and a valid ContentSlice.

editor.api.dom.clipboard.writeSelection(data: Pick<DataTransfer, 'getData' | 'setData'>): void

Write the current selection to a DataTransfer.

editor.api.dom.clipboard.writeSlice(data: Pick<DataTransfer, 'getData' | 'setData'>, payload: ClipboardSliceWrite): void

Write one exact ContentSlice plus optional browser MIME representations.

Plate writes plain text, HTML, and an internal editor fragment payload. The fragment payload uses application/${clipboardFormatKey} and the HTML fallback is tagged with the same key, so differently configured editors do not blindly import each other's internal JSON. Use Clipboard for copy, paste, drop, DataTransfer format ordering, and fitted slice replacement.

Resolution failures

resolve* methods return null when a model position has no current mounted DOM representation. The corresponding assert* methods throw DOMResolutionError. Import the error from platejs/dom when a boundary needs to handle it.

The document can contain hidden, external, or virtualized content. Configure content boundaries, external text, or virtualization on their owning components. A successful model lookup does not prove that its DOM is mounted.

Host utilities

platejs/dom exports isHotkey for platform-aware keyboard matching and domCommands, dataTransferFormats, and writeDataTransferFragment for browser transfer integration. Semantic HTML, Markdown, and plain-text node mappings belong in the owning plugin's formats declaration.

Its public types describe DOM primitives (DOMNode, DOMElement, DOMText, DOMPoint, DOMRange, DOMStaticRange, DOMSelection), mounted editor services (DOMApi, DOMClipboardApi), clipboard slices, DataTransfer formats, keyboard matching, visual caret movement, and scrolling.

See Selection for the interaction between model selection and the browser, and Editor Components for mounting the view.