insertCompositionText

How insertCompositionText inputType handles IME composition text insertion and varies across browsers.

Overview

The insertCompositionText inputType is triggered during IME (Input Method Editor) composition when text is being composed. This is particularly relevant for languages that require composition, such as Chinese, Japanese, and Korean.

Basic Behavior

Scenario: Korean IME composition

During composition (typing "한")

HTML:
<p>Hello |</p>
Composition text is shown with underline or highlight

After committing (pressing Enter/Space)

HTML:
<p>Hello |</p>
Composition text committed to final text

IME Composition Flow

1. Composition Start

  • compositionstart event fires
  • User begins typing characters that require composition

2. Composition Update

  • compositionupdate event fires
  • insertCompositionText inputType may fire in beforeinput or input events
  • Composition text is shown (often with underline or highlight)

3. Composition End

  • compositionend event fires
  • Final text is committed to the DOM
  • May trigger insertText or insertCompositionText events

Browser-Specific Behavior

Chrome/Edge

  • Fires insertCompositionText during composition updates
  • Composition text is shown with underline styling
  • May fire multiple insertCompositionText events as user types

Firefox

  • Similar behavior to Chrome
  • Composition handling may differ slightly

Safari

  • IME composition behavior may differ, especially on macOS
  • May use different event patterns

Critical Edge Cases

⚠️ macOS Korean IME + Formatting Shortcuts

macOS + Korean IME: During IME composition with a collapsed cursor, pressing formatting shortcuts (e.g., Cmd + B) may cause unexpected behavior:

  • Formatting beforeinput event may not fire
  • insertCompositionText may fire instead
  • Composition may be cancelled or committed partially

See formatBold - IME Composition for detailed workarounds.

⚠️ Composition Cancellation

Pressing certain keys during composition (e.g., Backspace, Delete, Escape) may cancel the composition:

  • Composition text may be lost
  • More text than expected may be deleted
  • Event order may be unpredictable

See deleteContentBackward - IME Composition for details.

Editor-Specific Handling

Different editor frameworks handle IME composition differently. Here's how major editors implement insertCompositionText:

Slate.js

IME Composition Handling

Slate tracks composition state and handles composition text:

    import { Editor, Transforms } from 'slate';

let isComposing = false;

element.addEventListener('compositionstart', () => {
  isComposing = true;
});

element.addEventListener('beforeinput', (e) => {
  if (e.inputType === 'insertCompositionText' && e.data) {
    e.preventDefault();
    
    // Insert composition text
    Transforms.insertText(editor, e.data);
  }
});

element.addEventListener('compositionend', () => {
  isComposing = false;
  // Finalize composition text if needed
});
  
  • Composition state: Tracks composition state separately from regular text insertion.
  • Text insertion: Uses Transforms.insertText() to insert composition text.
  • State management: Maintains composition state to handle edge cases.
ProseMirror

IME Composition Handling

ProseMirror uses composition state in the view:

    view.dom.addEventListener('compositionstart', () => {
  view.composing = true;
});

view.dom.addEventListener('beforeinput', (e) => {
  if (e.inputType === 'insertCompositionText' && e.data) {
    e.preventDefault();
    const { state, dispatch } = view;
    
    // Insert composition text
    if (insertText(state, e.data, state.selection.$from.marks(), dispatch)) {
      // Handled
    }
  }
});

view.dom.addEventListener('compositionend', () => {
  view.composing = false;
});
  
  • View.composing: ProseMirror tracks composition state in view.composing.
  • insertText command: Uses same insertText command as regular text insertion.
Draft.js

IME Composition Handling

Draft.js handles composition through input events:

    let isComposing = false;

element.addEventListener('compositionstart', () => {
  isComposing = true;
});

element.addEventListener('beforeinput', (e) => {
  if (e.inputType === 'insertCompositionText' && e.data) {
    e.preventDefault();
    
    const contentState = editorState.getCurrentContent();
    const selection = editorState.getSelection();
    
    const newContentState = Modifier.insertText(
      contentState,
      selection,
      e.data,
      editorState.getCurrentInlineStyle()
    );
    
    const newState = EditorState.push(
      editorState,
      newContentState,
      'insert-characters'
    );
    
    setEditorState(newState);
  }
});

element.addEventListener('compositionend', () => {
  isComposing = false;
});
  
  • Composition tracking: Tracks composition state to handle edge cases.
  • Modifier.insertText: Uses Modifier.insertText() to insert composition text.

Related resources