Overview
The insertFromDrop inputType is triggered when the user drops content into a contenteditable element via drag-and-drop. The browser inserts the dropped content at the drop position.
Basic Behavior
Scenario: Text dropped at cursor
Before (Cursor position)
HTML:
<p>Hello|world</p>
After dropping "text "
HTML:
<p>Hellotext world</p>
Dropped content inserted at drop position
DataTransfer Access
The beforeinput event provides access to dropped data via e.dataTransfer:
e.dataTransfer.getData('text/plain')- Plain texte.dataTransfer.getData('text/html')- HTML contente.dataTransfer.files- File list (for images, etc.)e.dataTransfer.types- Available data types
Browser-Specific Behavior
Chrome/Edge
- Preserves HTML structure when dropping rich text
- Supports dropping images as
<img>elements - May sanitize certain HTML elements for security
Firefox
- May strip or modify HTML structure more aggressively
- File handling may differ from Chrome
Safari
- Drag-and-drop behavior may differ, especially on mobile
- File handling may require additional permissions
Mobile Considerations
ℹ️ Mobile Drag-and-Drop
On mobile devices, drag-and-drop behavior may differ significantly:
- Touch-based drag-and-drop may not work the same way as mouse-based drag-and-drop
- Some mobile browsers may not support
insertFromDropinputType - File dropping from external apps may require special handling
Editor-Specific Handling
Different editor frameworks handle drop operations similarly to paste operations. Here's how major editors implement insertFromDrop:
Slate.js
Drop Handling
Slate intercepts drop and converts dropped content to Slate nodes:
import { Editor, Transforms } from 'slate';
element.addEventListener('beforeinput', (e) => {
if (e.inputType === 'insertFromDrop' && e.dataTransfer) {
e.preventDefault();
const htmlString = e.dataTransfer.getData('text/html');
const plainText = e.dataTransfer.getData('text/plain');
// Parse HTML and convert to Slate nodes (similar to paste)
const fragment = new DOMParser().parseFromString(htmlString || plainText, 'text/html');
const nodes = Array.from(fragment.body.childNodes).map(node => {
// Convert DOM node to Slate node structure
return jsx('element', { type: 'paragraph' }, [{ text: node.textContent || '' }]);
});
// Insert nodes at drop position
Transforms.insertNodes(editor, nodes);
}
});
- Similar to paste: Uses same HTML parsing and conversion logic as paste operations.
- Position handling: Drop position is determined by the drop event coordinates.
ProseMirror
Drop Handling
ProseMirror uses DOMParser to parse dropped HTML:
import { DOMParser } from 'prosemirror-model';
import { schema } from './schema';
view.dom.addEventListener('beforeinput', (e) => {
if (e.inputType === 'insertFromDrop' && e.dataTransfer) {
e.preventDefault();
const { state, dispatch } = view;
const htmlString = e.dataTransfer.getData('text/html');
const dom = new DOMParser(window).parseFromString(htmlString, 'text/html');
// Parse HTML into ProseMirror nodes
const fragment = DOMParser.fromSchema(schema).parse(dom.body);
// Insert fragment at drop position
const tr = state.tr.replaceSelection(fragment);
dispatch(tr);
}
});
- Schema-aware: HTML is parsed according to ProseMirror schema rules.
- Position calculation: Drop position is calculated from event coordinates.
Draft.js
Drop Handling
Draft.js uses convertFromHTML to convert dropped HTML:
import { EditorState } from 'draft-js';
import { stateFromHTML } from 'draft-js-import-html';
element.addEventListener('beforeinput', (e) => {
if (e.inputType === 'insertFromDrop' && e.dataTransfer) {
e.preventDefault();
const htmlString = e.dataTransfer.getData('text/html');
// Convert HTML to ContentState
const contentState = stateFromHTML(htmlString);
// Get drop position from selection
const selection = editorState.getSelection();
// Insert content at drop position
const newContentState = Modifier.replaceWithFragment(
editorState.getCurrentContent(),
selection,
contentState.getBlockMap()
);
const newState = EditorState.push(
editorState,
newContentState,
'insert-fragment'
);
setEditorState(newState);
}
});
- HTML import: Uses
stateFromHTMLto convert HTML to ContentState. - Position handling: Drop position is determined from selection state.