Overview
The formatUnderline inputType is triggered when the user presses Ctrl/Cmd + U. The browser wraps selected text in a <u> element.
Basic Behavior
Scenario: Text selected within paragraph
Before (Text selected)
After Cmd/Ctrl + U
Collapsed Selection
⚠️ Critical Edge Case
When the selection is collapsed, pressing Cmd/Ctrl + U toggles underline state for the next character. input event typically does not fire.
IME Composition + formatUnderline
⚠️ Critical Issue
During IME composition, pressing Cmd/Ctrl + U may not work as expected. On macOS with Korean IME, formatUnderline does not fire; insertCompositionText may fire instead.
See formatBold for detailed workarounds.
Editor-Specific Handling
Different editor frameworks handle underline formatting similarly to other text formatting. Here's how major editors implement formatUnderline:
Underline Formatting
Slate uses Transforms.setNodes() to toggle underline marks:
import { Editor, Transforms, Text } from 'slate';
function isUnderlineActive(editor: Editor) {
const marks = Editor.marks(editor);
return marks?.underline === true;
}
function handleFormatUnderline(editor: Editor) {
const isActive = isUnderlineActive(editor);
Transforms.setNodes(
editor,
{ underline: !isActive },
{ match: n => Text.isText(n), split: true }
);
}
element.addEventListener('beforeinput', (e) => {
if (e.inputType === 'formatUnderline') {
e.preventDefault();
handleFormatUnderline(editor);
}
});
- Mark-based: Formatting stored as
{ underline: true }on text nodes. - Same pattern: Uses identical Transforms API as other formatting marks.
Underline Formatting
ProseMirror uses toggleMark with underline mark:
import { toggleMark } from 'prosemirror-commands';
import { schema } from './schema';
function handleFormatUnderline(state, dispatch) {
return toggleMark(schema.marks.underline)(state, dispatch);
}
view.dom.addEventListener('beforeinput', (e) => {
if (e.inputType === 'formatUnderline') {
e.preventDefault();
const { state, dispatch } = view;
if (handleFormatUnderline(state, dispatch)) {
// Handled
}
}
});
- Mark system: Uses
schema.marks.underlinemark type. - Same command: Uses
toggleMark()like other formatting.
Underline Formatting
Draft.js uses RichUtils.toggleInlineStyle() with UNDERLINE:
import { EditorState, RichUtils } from 'draft-js';
function handleFormatUnderline(editorState) {
return RichUtils.toggleInlineStyle(editorState, 'UNDERLINE');
}
function myKeyBindingFn(e) {
if (e.keyCode === 85 && (e.ctrlKey || e.metaKey)) {
return 'underline';
}
return getDefaultKeyBinding(e);
}
function handleKeyCommand(command) {
if (command === 'underline') {
const newState = handleFormatUnderline(editorState);
setEditorState(newState);
return 'handled';
}
return 'not-handled';
}
- Inline style: Uses
'UNDERLINE'style string. - Same utility: Uses
RichUtils.toggleInlineStylelike other formatting.