From zbeyens. The source code is available on GitHub.

Plate
PlatePliteEditorsTemplates
GitHub16kGitHub
DiscordDiscord
    • Stream
    • Copilot
  • Discussion
    • Comments
    • Suggestion
    • Basic Blocks
      • Blockquote
      • Heading
      • Horizontal Rule
    • Callout
    • Code Block
    • Column
    • Date
    • Equation
    • Link
    • List Classic
    • Media
    • MentionElement
    • Table
    • Table of Contents
    • Footnote
    • Toggle
  • Marks
    • Bold
    • Italic
    • Underline
    • Code
    • Highlight
    • Keyboard Input
    • Strikethrough
    • Subscript
    • Superscript
      • Font
      • Line Height
      • Text Align
    • Indent
    • List
      • Exit Break
      • Single Block
      • Trailing Block
    • Autoformat
    • Block Menu
    • Block Placeholder
    • Combobox
      • Emoji
      • MentionElement
      • Slash Command
    • Cursor Overlay
    • Drag & Drop
    • Navigation Feedback
    • Tabbable
    • Toolbar
    • Yjs
    • Multi SelectEditor
    • CSV
    • DOCX
    • HTML
    • Markdown

Underline

PreviousNext

Add underlined inline text.

Basic MarksMark Toolbar ButtonPlugin Shortcuts

Underline applies the underline leaf mark to selected text. UnderlinePlugin owns the mark key, shortcut, HTML parsing, render tag, and toggle transform.

Loading…
ItalicCode

On This Page

FeaturesKit UsageAdd Basic MarksAdd A Toolbar ButtonManual UsageOwnershipBehaviorAPI Reference
Build your editor
Production-ready AI template and reusable components.
Get all-access

Features

  • Boolean underline leaf property owned by the PLUGINS.underline capability.
  • Default ⌘U / Ctrl+U shortcut.
  • HTML deserialization from u and underline text decoration.
  • <u> rendering by default.
  • Optional Markdown-style input rule through UnderlineRules.
  • Toolbar support through MarkToolbarButton.
Report an issue

Kit Usage

Add Basic Marks

BasicMarksKit includes UnderlinePlugin, the underscore input rule, and the standard toolbar buttons that use the plugin descriptor.

'use client';
 
import {
  BoldRules,
  CodeRules,
  HighlightRules,
  ItalicRules,
  MarkComboRules,
  ScriptRules,
  StrikethroughRules,
  UnderlineRules,
} from '@platejs/basic-nodes';
import {
  BoldPlugin,
  CodePlugin,
  HighlightPlugin,
  ItalicPlugin,
  KbdPlugin,
  ScriptPlugin,
  StrikethroughPlugin,
  UnderlinePlugin,
} from '@platejs/basic-nodes/react';
 
import { CodeLeaf } from '@/components/editor/code';
import { HighlightLeaf } from '@/components/editor/highlight';
import { KbdLeaf } from '@/components/editor/kbd';
 
export const BasicMarksKit = [
  BoldPlugin.configure({
    inputRules: [
      BoldRules.markdown({ variant: '*' }),
      BoldRules.markdown({ variant: '_' }),
      MarkComboRules.markdown({ variant: 'boldItalic' }),
      MarkComboRules.markdown({ variant: 'boldUnderline' }),
      MarkComboRules.markdown({ variant: 'boldItalicUnderline' }),
      MarkComboRules.markdown({ variant: 'italicUnderline' }),
    ],
  }),
  ItalicPlugin.configure({
    inputRules: [
      ItalicRules.markdown({ variant: '*' }),
      ItalicRules.markdown({ variant: '_' }),
    ],
  }),
  UnderlinePlugin.configure({
    inputRules: [UnderlineRules.markdown()],
  }),
  CodePlugin.configure({
    component: CodeLeaf,
    inputRules: [CodeRules.markdown()],
    shortcuts: { toggle: { keys: 'mod+e' } },
  }),
  StrikethroughPlugin.configure({
    inputRules: [StrikethroughRules.markdown()],
    shortcuts: { toggle: { keys: 'mod+shift+x' } },
  }),
  ScriptPlugin.configure({
    inputRules: [
      ScriptRules.markdown({ value: 'sub' }),
      ScriptRules.markdown({ value: 'sup' }),
    ],
    shortcuts: {
      subscript: {
        keys: 'mod+comma',
        handler: ({ editor }) => {
          editor.plugin(ScriptPlugin).update.toggle('sub');
        },
      },
      superscript: {
        keys: 'mod+period',
        handler: ({ editor }) => {
          editor.plugin(ScriptPlugin).update.toggle('sup');
        },
      },
    },
  }),
  HighlightPlugin.configure({
    component: HighlightLeaf,
    inputRules: [
      HighlightRules.markdown({ variant: '==' }),
      HighlightRules.markdown({ variant: '≡' }),
    ],
    shortcuts: { toggle: { keys: 'mod+shift+h' } },
  }),
  KbdPlugin.configure({ component: KbdLeaf }),
] as const;
'use client';
 
import {
  BoldRules,
  CodeRules,
  HighlightRules,
  ItalicRules,
  MarkComboRules,
  ScriptRules,
  StrikethroughRules,
  UnderlineRules,
} from '@platejs/basic-nodes';
import {
  BoldPlugin,
  CodePlugin,
  HighlightPlugin,
  ItalicPlugin,
  KbdPlugin,
  ScriptPlugin,
  StrikethroughPlugin,
  UnderlinePlugin,
} from '@platejs/basic-nodes/react';
 
import { CodeLeaf } from '@/components/editor/code';
import { HighlightLeaf } from '@/components/editor/highlight';




























































import { createPlateEditor } from 'platejs/react';
 
import { BasicMarksKit } from '@/components/editor/basic-marks';
 
export const editor = createPlateEditor({
  plugins: [...BasicMarksKit],
});
import { createPlateEditor } from 'platejs/react';
 
import { BasicMarksKit } from '@/components/editor/basic-marks';
 
export const editor = createPlateEditor({

Add A Toolbar Button

Pass UnderlinePlugin to MarkToolbarButton so the button uses the configured mark key.

import { UnderlineIcon } from 'lucide-react';
import { UnderlinePlugin } from '@platejs/basic-nodes/react';
 
import { MarkToolbarButton } from '@/components/editor/mark-toolbar-button';
 
export function UnderlineToolbarButton() {
  return (
    <MarkToolbarButton plugin={UnderlinePlugin} tooltip="Underline (⌘+U)">
      <UnderlineIcon />
    </MarkToolbarButton>
  );
}

Manual Usage

Install the mark package.

pnpm add @platejs/basic-nodes
pnpm add @platejs/basic-nodes

Add UnderlinePlugin directly when you do not want the whole kit.

import { UnderlinePlugin } from '@platejs/basic-nodes/react';
import { createPlateEditor } from 'platejs/react';
 
export const editor = createPlateEditor({
  plugins: [UnderlinePlugin],
});
import { UnderlinePlugin } from '@platejs/basic-nodes/react';
import { createPlateEditor } from 'platejs/react';
 
export const editor = createPlateEditor({
  plugins: [UnderlinePlugin],
});

Register the Markdown-style input rule when users should type underline delimiters.

import { UnderlineRules } from '@platejs/basic-nodes';
import { UnderlinePlugin } from '@platejs/basic-nodes/react';
 
export const underlinePlugin = UnderlinePlugin.configure({
  inputRules: [UnderlineRules.markdown()],
});
import { UnderlineRules } from '@platejs/basic-nodes';
import { UnderlinePlugin } from '@platejs/basic-nodes/react';
 
export const underlinePlugin = UnderlinePlugin.configure({
  inputRules: [UnderlineRules.markdown()],
});

Ownership

SurfaceOwnerWhat It Does
BaseUnderlinePlugin@platejs/basic-nodesHeadless underline mark, HTML parser, render tag, and toggle transform.
UnderlinePlugin@platejs/basic-nodes/reactReact wrapper with default mod+u shortcut.
UnderlineRules.markdown@platejs/basic-nodesOptional mark input rule factory.
BasicMarksKitRegistryAdds UnderlinePlugin with the underscore input rule.
MarkToolbarButtonRegistry UIReads active mark state and calls the mark toggle hook.

The package owns the mark. The registry owns toolbar placement and icon choice.

Behavior

BehaviorSource
Mark keyeditor.plugin(UnderlinePlugin).schema.key (default: underline)
Mark declarationschema.mark: property.boolean({ default: false, omitDefault: true })
Toggle commandtx.underline.toggle() toggles the resolved schema key.
ShortcutUnderlinePlugin registers mod+u.
HTML tagsu
HTML stylestext-decoration: underline
HTML guardIgnores descendants where textDecoration is none.
Render outputu

API Reference

APIPackageUse
BaseUnderlinePlugin@platejs/basic-nodesHeadless underline plugin.
UnderlinePlugin@platejs/basic-nodes/reactReact underline plugin with shortcut defaults.
UnderlineRules.markdown()@platejs/basic-nodesCreates the underline mark input rule.
tx.underline.toggle()@platejs/basic-nodesToggles the underline mark at the selection.
import { KbdLeaf } from '@/components/editor/kbd';
export const BasicMarksKit = [
BoldPlugin.configure({
inputRules: [
BoldRules.markdown({ variant: '*' }),
BoldRules.markdown({ variant: '_' }),
MarkComboRules.markdown({ variant: 'boldItalic' }),
MarkComboRules.markdown({ variant: 'boldUnderline' }),
MarkComboRules.markdown({ variant: 'boldItalicUnderline' }),
MarkComboRules.markdown({ variant: 'italicUnderline' }),
],
}),
ItalicPlugin.configure({
inputRules: [
ItalicRules.markdown({ variant: '*' }),
ItalicRules.markdown({ variant: '_' }),
],
}),
UnderlinePlugin.configure({
inputRules: [UnderlineRules.markdown()],
}),
CodePlugin.configure({
component: CodeLeaf,
inputRules: [CodeRules.markdown()],
shortcuts: { toggle: { keys: 'mod+e' } },
}),
StrikethroughPlugin.configure({
inputRules: [StrikethroughRules.markdown()],
shortcuts: { toggle: { keys: 'mod+shift+x' } },
}),
ScriptPlugin.configure({
inputRules: [
ScriptRules.markdown({ value: 'sub' }),
ScriptRules.markdown({ value: 'sup' }),
],
shortcuts: {
subscript: {
keys: 'mod+comma',
handler: ({ editor }) => {
editor.plugin(ScriptPlugin).update.toggle('sub');
},
},
superscript: {
keys: 'mod+period',
handler: ({ editor }) => {
editor.plugin(ScriptPlugin).update.toggle('sup');
},
},
},
}),
HighlightPlugin.configure({
component: HighlightLeaf,
inputRules: [
HighlightRules.markdown({ variant: '==' }),
HighlightRules.markdown({ variant: '≡' }),
],
shortcuts: { toggle: { keys: 'mod+shift+h' } },
}),
KbdPlugin.configure({ component: KbdLeaf }),
] as const;
plugins: [
...
BasicMarksKit],
});
import { UnderlineIcon } from 'lucide-react'; import { UnderlinePlugin } from '@platejs/basic-nodes/react'; import { MarkToolbarButton } from '@/components/editor/mark-toolbar-button'; export function UnderlineToolbarButton() { return ( <MarkToolbarButton plugin={UnderlinePlugin} tooltip="Underline (⌘+U)"> <UnderlineIcon /> </MarkToolbarButton> ); }