A React framework for building text editors with immutable data models.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Utilities for making changes to editor content with proper immutable updates. These tools provide both low-level content manipulation and high-level rich text operations.
Low-level utility functions for modifying ContentState objects. All methods return new immutable ContentState instances.
/**
* Low-level content modification utilities
*/
const Modifier: {
/**
* Replace text in specified range
* @param contentState - Current content state
* @param targetRange - Selection range to replace
* @param text - Replacement text
* @param inlineStyle - Optional inline styles to apply
* @param entityKey - Optional entity key to apply
* @returns New ContentState with text replaced
*/
replaceText(
contentState: ContentState,
targetRange: SelectionState,
text: string,
inlineStyle?: DraftInlineStyle,
entityKey?: ?string
): ContentState;
/**
* Insert text at specified range (collapses range first if needed)
* @param contentState - Current content state
* @param targetRange - Selection range for insertion
* @param text - Text to insert
* @param inlineStyle - Optional inline styles to apply
* @param entityKey - Optional entity key to apply
* @returns New ContentState with text inserted
*/
insertText(
contentState: ContentState,
targetRange: SelectionState,
text: string,
inlineStyle?: DraftInlineStyle,
entityKey?: ?string
): ContentState;
/**
* Move text from one location to another
* @param contentState - Current content state
* @param removalRange - Range of text to move
* @param targetRange - Destination for moved text
* @returns New ContentState with text moved
*/
moveText(
contentState: ContentState,
removalRange: SelectionState,
targetRange: SelectionState
): ContentState;
/**
* Replace range with content fragment
* @param contentState - Current content state
* @param targetRange - Range to replace
* @param fragment - Content blocks to insert
* @returns New ContentState with fragment inserted
*/
replaceWithFragment(
contentState: ContentState,
targetRange: SelectionState,
fragment: BlockMap
): ContentState;
/**
* Remove content in specified range
* @param contentState - Current content state
* @param targetRange - Range to remove
* @param removalDirection - Direction of removal (forward/backward)
* @returns New ContentState with content removed
*/
removeRange(
contentState: ContentState,
targetRange: SelectionState,
removalDirection: DraftRemovalDirection
): ContentState;
/**
* Split block at specified position
* @param contentState - Current content state
* @param targetRange - Position to split (should be collapsed)
* @returns New ContentState with block split
*/
splitBlock(
contentState: ContentState,
targetRange: SelectionState
): ContentState;
/**
* Apply inline style to range
* @param contentState - Current content state
* @param targetRange - Range to style
* @param inlineStyle - Style name to apply
* @returns New ContentState with style applied
*/
applyInlineStyle(
contentState: ContentState,
targetRange: SelectionState,
inlineStyle: string
): ContentState;
/**
* Remove inline style from range
* @param contentState - Current content state
* @param targetRange - Range to modify
* @param inlineStyle - Style name to remove
* @returns New ContentState with style removed
*/
removeInlineStyle(
contentState: ContentState,
targetRange: SelectionState,
inlineStyle: string
): ContentState;
/**
* Set block type for blocks in range
* @param contentState - Current content state
* @param targetRange - Range of blocks to modify
* @param blockType - New block type
* @returns New ContentState with block types changed
*/
setBlockType(
contentState: ContentState,
targetRange: SelectionState,
blockType: DraftBlockType
): ContentState;
/**
* Set block data for blocks in range
* @param contentState - Current content state
* @param targetRange - Range of blocks to modify
* @param blockData - New block data
* @returns New ContentState with block data set
*/
setBlockData(
contentState: ContentState,
targetRange: SelectionState,
blockData: Map<any, any>
): ContentState;
/**
* Merge block data for blocks in range
* @param contentState - Current content state
* @param targetRange - Range of blocks to modify
* @param blockData - Block data to merge
* @returns New ContentState with block data merged
*/
mergeBlockData(
contentState: ContentState,
targetRange: SelectionState,
blockData: Map<any, any>
): ContentState;
/**
* Apply entity to range
* @param contentState - Current content state
* @param targetRange - Range to apply entity
* @param entityKey - Entity key to apply (null to remove)
* @returns New ContentState with entity applied
*/
applyEntity(
contentState: ContentState,
targetRange: SelectionState,
entityKey: ?string
): ContentState;
};Usage Examples:
import { Modifier, EditorState, SelectionState } from "draft-js";
function insertTextExample(editorState) {
const contentState = editorState.getCurrentContent();
const selection = editorState.getSelection();
// Insert text with bold styling
const newContentState = Modifier.insertText(
contentState,
selection,
"Hello World!",
OrderedSet(['BOLD'])
);
return EditorState.push(editorState, newContentState, 'insert-characters');
}
function applyStyleExample(editorState) {
const contentState = editorState.getCurrentContent();
const selection = editorState.getSelection();
// Apply italic style to selected text
const newContentState = Modifier.applyInlineStyle(
contentState,
selection,
'ITALIC'
);
return EditorState.push(editorState, newContentState, 'change-inline-style');
}
function changeBlockTypeExample(editorState) {
const contentState = editorState.getCurrentContent();
const selection = editorState.getSelection();
// Change current block to header
const newContentState = Modifier.setBlockType(
contentState,
selection,
'header-one'
);
return EditorState.push(editorState, newContentState, 'change-block-type');
}High-level utilities for common rich text editing operations. These functions work directly with EditorState.
/**
* High-level rich text editing utilities
*/
const RichUtils: {
/**
* Handle keyboard commands (bold, italic, undo, etc.)
* @param editorState - Current editor state
* @param command - Command string from key binding
* @returns New EditorState or null if not handled
*/
handleKeyCommand(editorState: EditorState, command: DraftEditorCommand): ?EditorState;
/**
* Toggle inline style (add if not present, remove if present)
* @param editorState - Current editor state
* @param inlineStyle - Style name to toggle
* @returns New EditorState with style toggled
*/
toggleInlineStyle(editorState: EditorState, inlineStyle: string): EditorState;
/**
* Toggle block type
* @param editorState - Current editor state
* @param blockType - Block type to toggle to
* @returns New EditorState with block type changed
*/
toggleBlockType(editorState: EditorState, blockType: DraftBlockType): EditorState;
/**
* Toggle link entity on selection
* @param editorState - Current editor state
* @param targetSelection - Selection to apply link to
* @param entityKey - Link entity key (null to remove link)
* @returns New EditorState with link toggled
*/
toggleLink(
editorState: EditorState,
targetSelection: SelectionState,
entityKey: ?string
): EditorState;
/**
* Insert soft newline (line break within block)
* @param editorState - Current editor state
* @returns New EditorState with soft newline inserted
*/
insertSoftNewline(editorState: EditorState): EditorState;
/**
* Handle backspace key press
* @param editorState - Current editor state
* @returns New EditorState or null if not handled
*/
onBackspace(editorState: EditorState): ?EditorState;
/**
* Handle delete key press
* @param editorState - Current editor state
* @returns New EditorState or null if not handled
*/
onDelete(editorState: EditorState): ?EditorState;
/**
* Handle tab key press (for list indentation)
* @param event - Keyboard event
* @param editorState - Current editor state
* @param maxDepth - Maximum nesting depth
* @returns New EditorState with depth adjusted
*/
onTab(
event: SyntheticKeyboardEvent,
editorState: EditorState,
maxDepth: number
): EditorState;
/**
* Toggle code formatting
* @param editorState - Current editor state
* @returns New EditorState with code toggled
*/
toggleCode(editorState: EditorState): EditorState;
/**
* Get current block type
* @param editorState - Current editor state
* @returns Block type of current block
*/
getCurrentBlockType(editorState: EditorState): DraftBlockType;
/**
* Check if current block contains link
* @param editorState - Current editor state
* @returns True if current selection contains a link
*/
currentBlockContainsLink(editorState: EditorState): boolean;
/**
* Try to remove block-level styling
* @param editorState - Current editor state
* @returns New ContentState or null if no change
*/
tryToRemoveBlockStyle(editorState: EditorState): ?ContentState;
};Usage Examples:
import { RichUtils, EditorState } from "draft-js";
// Handle keyboard commands
function handleKeyCommand(command, editorState) {
const newState = RichUtils.handleKeyCommand(editorState, command);
if (newState) {
return newState;
}
return editorState;
}
// Toggle formatting
function toggleBold(editorState) {
return RichUtils.toggleInlineStyle(editorState, 'BOLD');
}
function toggleItalic(editorState) {
return RichUtils.toggleInlineStyle(editorState, 'ITALIC');
}
function toggleHeader(editorState) {
return RichUtils.toggleBlockType(editorState, 'header-one');
}
// Check current state
function isCurrentBlockHeader(editorState) {
const blockType = RichUtils.getCurrentBlockType(editorState);
return blockType.startsWith('header-');
}
// Handle special keys
function handleTab(e, editorState) {
const newState = RichUtils.onTab(e, editorState, 4);
return newState;
}
function handleReturn(e, editorState) {
if (e.shiftKey) {
return RichUtils.insertSoftNewline(editorState);
}
return editorState;
}Utilities for working with atomic blocks - blocks that contain non-text content like images, videos, or embeds.
/**
* Utilities for atomic blocks (media, embeds, etc.)
*/
const AtomicBlockUtils: {
/**
* Insert atomic block at current selection
* @param editorState - Current editor state
* @param entityKey - Entity key for the atomic content
* @param character - Character to represent the atomic block
* @returns New EditorState with atomic block inserted
*/
insertAtomicBlock(
editorState: EditorState,
entityKey: string,
character: string
): EditorState;
/**
* Move atomic block to new position
* @param editorState - Current editor state
* @param atomicBlock - The atomic block to move
* @param targetRange - Destination selection
* @param insertionMode - How to insert ('before' | 'after')
* @returns New EditorState with atomic block moved
*/
moveAtomicBlock(
editorState: EditorState,
atomicBlock: ContentBlock,
targetRange: SelectionState,
insertionMode?: DraftInsertionType
): EditorState;
};Usage Examples:
import { AtomicBlockUtils, Entity, EditorState } from "draft-js";
// Insert image
function insertImage(editorState, src, alt) {
// Create entity for the image
const entityKey = Entity.create('IMAGE', 'IMMUTABLE', {
src: src,
alt: alt
});
// Insert atomic block
return AtomicBlockUtils.insertAtomicBlock(
editorState,
entityKey,
' ' // Space character as placeholder
);
}
// Insert video embed
function insertVideo(editorState, videoUrl) {
const entityKey = Entity.create('VIDEO', 'IMMUTABLE', {
src: videoUrl
});
return AtomicBlockUtils.insertAtomicBlock(
editorState,
entityKey,
'📹' // Video emoji as placeholder
);
}
// Move atomic block
function moveAtomicBlockUp(editorState, atomicBlock) {
const selection = editorState.getSelection();
const targetRange = selection.merge({
anchorKey: atomicBlock.getKey(),
focusKey: atomicBlock.getKey()
});
return AtomicBlockUtils.moveAtomicBlock(
editorState,
atomicBlock,
targetRange,
'before'
);
}// Removal direction
type DraftRemovalDirection = 'backward' | 'forward';
// Insertion type for atomic blocks
type DraftInsertionType = 'replace' | 'before' | 'after';
// Editor commands
type DraftEditorCommand = 'backspace' | 'backspace-word' | 'backspace-to-start-of-line' |
'delete' | 'delete-word' | 'delete-to-end-of-block' | 'enter' | 'forward-delete' |
'insert-characters' | 'insert-compose-character' | 'move-selection-to-start-of-block' |
'move-selection-to-end-of-block' | 'secondary-cut' | 'secondary-paste' | 'split-block' |
'transpose-characters' | 'undo' | 'redo' | 'bold' | 'code' | 'italic' | 'strikethrough' |
'underline';
// Block merge behavior
type BlockDataMergeBehavior = 'REPLACE_WITH_NEW_DATA' | 'MERGE_OLD_DATA_TO_NEW_DATA';
// Entity mutability
type DraftEntityMutability = 'MUTABLE' | 'IMMUTABLE' | 'SEGMENTED';Install with Tessl CLI
npx tessl i tessl/npm-draft-js