Autoformat

Automatic text formatting via shortcodes and markdown-like shortcuts.

Loading...
Files
components/demo.tsx
'use client';

import * as React from 'react';

import { Plate, usePlateEditor } from 'platejs/react';

import { EditorKit } from '@/components/editor/editor-kit';
import { Editor, EditorContainer } from '@/components/ui/editor';

import { DEMO_VALUES } from './values/demo-values';

export default function Demo({ id }: { id: string }) {
  const editor = usePlateEditor({
    plugins: EditorKit,
    value: DEMO_VALUES[id],
  });

  return (
    <Plate editor={editor}>
      <EditorContainer variant="demo">
        <Editor />
      </EditorContainer>
    </Plate>
  );
}

Features

  • Markdown-style shortcuts for blocks (e.g., # to H1, > for blockquote).
  • Inline mark formatting (e.g., **bold**, *italic*, ~~strikethrough~~).
  • Smart punctuation conversion (e.g., -- to , ... to ).
  • Mathematical symbols and fractions.
  • Legal symbols and arrows.
  • Undo support on delete to reverse autoformatting.

Kit Usage

Installation

The fastest way to add autoformatting is with the AutoformatKit, which includes comprehensive formatting rules for blocks, marks, and text replacements.

'use client';
 
import type { AutoformatRule } from '@platejs/autoformat';
 
import {
  autoformatArrow,
  autoformatLegal,
  autoformatLegalHtml,
  autoformatMath,
  AutoformatPlugin,
  autoformatPunctuation,
  autoformatSmartQuotes,
} from '@platejs/autoformat';
import { insertEmptyCodeBlock } from '@platejs/code-block';
import { toggleList } from '@platejs/list';
import { KEYS } from 'platejs';
 
const autoformatMarks: AutoformatRule[] = [
  {
    match: '***',
    mode: 'mark',
    type: [KEYS.bold, KEYS.italic],
  },
  {
    match: '__*',
    mode: 'mark',
    type: [KEYS.underline, KEYS.italic],
  },
  {
    match: '__**',
    mode: 'mark',
    type: [KEYS.underline, KEYS.bold],
  },
  {
    match: '___***',
    mode: 'mark',
    type: [KEYS.underline, KEYS.bold, KEYS.italic],
  },
  {
    match: '**',
    mode: 'mark',
    type: KEYS.bold,
  },
  {
    match: '__',
    mode: 'mark',
    type: KEYS.underline,
  },
  {
    match: '*',
    mode: 'mark',
    type: KEYS.italic,
  },
  {
    match: '_',
    mode: 'mark',
    type: KEYS.italic,
  },
  {
    match: '~~',
    mode: 'mark',
    type: KEYS.strikethrough,
  },
  {
    match: '^',
    mode: 'mark',
    type: KEYS.sup,
  },
  {
    match: '~',
    mode: 'mark',
    type: KEYS.sub,
  },
  {
    match: '==',
    mode: 'mark',
    type: KEYS.highlight,
  },
  {
    match: '≡',
    mode: 'mark',
    type: KEYS.highlight,
  },
  {
    match: '`',
    mode: 'mark',
    type: KEYS.code,
  },
];
 
const autoformatBlocks: AutoformatRule[] = [
  {
    match: '# ',
    mode: 'block',
    type: KEYS.h1,
  },
  {
    match: '## ',
    mode: 'block',
    type: KEYS.h2,
  },
  {
    match: '### ',
    mode: 'block',
    type: KEYS.h3,
  },
  {
    match: '#### ',
    mode: 'block',
    type: KEYS.h4,
  },
  {
    match: '##### ',
    mode: 'block',
    type: KEYS.h5,
  },
  {
    match: '###### ',
    mode: 'block',
    type: KEYS.h6,
  },
  {
    match: '> ',
    mode: 'block',
    type: KEYS.blockquote,
  },
  {
    match: '```',
    mode: 'block',
    type: KEYS.codeBlock,
    format: (editor) => {
      insertEmptyCodeBlock(editor, {
        defaultType: KEYS.p,
        insertNodesOptions: { select: true },
      });
    },
  },
  // {
  //   match: '+ ',
  //   mode: 'block',
  //   preFormat: openNextToggles,
  //   type: KEYS.toggle,
  // },
  {
    match: ['---', '—-', '___ '],
    mode: 'block',
    type: KEYS.hr,
    format: (editor) => {
      editor.tf.setNodes({ type: KEYS.hr });
      editor.tf.insertNodes({
        children: [{ text: '' }],
        type: KEYS.p,
      });
    },
  },
];
 
const autoformatLists: AutoformatRule[] = [
  {
    match: ['* ', '- '],
    mode: 'block',
    type: 'list',
    format: (editor) => {
      toggleList(editor, {
        listStyleType: KEYS.ul,
      });
    },
  },
  {
    match: [String.raw`^\d+\.$ `, String.raw`^\d+\)$ `],
    matchByRegex: true,
    mode: 'block',
    type: 'list',
    format: (editor, { matchString }) => {
      toggleList(editor, {
        listRestartPolite: Number(matchString) || 1,
        listStyleType: KEYS.ol,
      });
    },
  },
  {
    match: ['[] '],
    mode: 'block',
    type: 'list',
    format: (editor) => {
      toggleList(editor, {
        listStyleType: KEYS.listTodo,
      });
      editor.tf.setNodes({
        checked: false,
        listStyleType: KEYS.listTodo,
      });
    },
  },
  {
    match: ['[x] '],
    mode: 'block',
    type: 'list',
    format: (editor) => {
      toggleList(editor, {
        listStyleType: KEYS.listTodo,
      });
      editor.tf.setNodes({
        checked: true,
        listStyleType: KEYS.listTodo,
      });
    },
  },
];
 
export const AutoformatKit = [
  AutoformatPlugin.configure({
    options: {
      enableUndoOnDelete: true,
      rules: [
        ...autoformatBlocks,
        ...autoformatMarks,
        ...autoformatSmartQuotes,
        ...autoformatPunctuation,
        ...autoformatLegal,
        ...autoformatLegalHtml,
        ...autoformatArrow,
        ...autoformatMath,
        ...autoformatLists,
      ].map(
        (rule): AutoformatRule => ({
          ...rule,
          query: (editor) =>
            !editor.api.some({
              match: { type: editor.getType(KEYS.codeBlock) },
            }),
        })
      ),
    },
  }),
];

Add Kit

import { createPlateEditor } from 'platejs/react';
import { AutoformatKit } from '@/components/editor/plugins/autoformat-kit';
 
const editor = createPlateEditor({
  plugins: [
    // ...otherPlugins,
    ...AutoformatKit,
  ],
});

Manual Usage

Installation

pnpm add @platejs/autoformat

Add Plugin

import { AutoformatPlugin } from '@platejs/autoformat';
import { createPlateEditor } from 'platejs/react';
 
const editor = createPlateEditor({
  plugins: [
    // ...otherPlugins,
    AutoformatPlugin,
  ],
});

Configure Plugin

Configure autoformat with custom rules:

import { AutoformatPlugin } from '@platejs/autoformat';
 
AutoformatPlugin.configure({
  options: {
    rules: [
      // Block rules
      {
        match: '# ',
        mode: 'block',
        type: 'h1',
      },
      {
        match: '> ',
        mode: 'block',
        type: 'blockquote',
      },
      // Mark rules
      {
        match: '**',
        mode: 'mark',
        type: 'bold',
      },
      {
        match: '*',
        mode: 'mark',
        type: 'italic',
      },
    ],
    enableUndoOnDelete: true,
  },
});

Advanced Configuration

Import predefined rule sets for comprehensive autoformatting:

import { AutoformatPlugin } from '@platejs/autoformat';
import {
  autoformatArrow,
  autoformatLegal,
  autoformatMath,
  autoformatPunctuation,
  autoformatSmartQuotes,
} from '@platejs/autoformat';
 
AutoformatPlugin.configure({
  options: {
    enableUndoOnDelete: true,
    rules: [
      // Custom block rules
      {
        match: '# ',
        mode: 'block',
        type: 'h1',
      },
      // Predefined rule sets
      ...autoformatSmartQuotes,
      ...autoformatPunctuation,
      ...autoformatArrow,
      ...autoformatLegal,
      ...autoformatMath,
    ].map((rule) => ({
      ...rule,
      // Disable autoformat in code blocks
      query: (editor) =>
        !editor.api.some({
          match: { type: 'code_block' },
        }),
    })),
  },
});
  • rules: Array of autoformat rules defining triggers and formatting actions.
  • enableUndoOnDelete: Allows undoing autoformat by pressing backspace.
  • query: Function to conditionally enable/disable rules based on context.

Using Regex Patterns

For more complex matching patterns, you can use regular expressions:

import { AutoformatPlugin } from '@platejs/autoformat';
import { toggleList } from '@platejs/list';
 
AutoformatPlugin.configure({
  options: {
    rules: [
      {
        match: [String.raw`^\d+\.$ `, String.raw`^\d+\)$ `],
        matchByRegex: true,
        mode: 'block',
        type: 'list',
        format: (editor, { matchString }) => {
          const number = Number(matchString.match(/\d+/)?.[0]) || 1;
          toggleList(editor, {
            listRestartPolite: number,
            listStyleType: 'ol',
          });
        },
      },
    ],
  },
});
  • matchByRegex: Enables regex pattern matching instead of string equality.
  • Note that Regex patterns only work with mode: 'block' and are applied at block start (triggerAtBlockStart: true).

Plugins

AutoformatPlugin

Plugin for automatic text formatting based on typing patterns.

Options

Collapse all

    A list of triggering rules. Can be one of: AutoformatBlockRule, AutoformatMarkRule, AutoformatTextRule. Each extends AutoformatCommonRule.

    • Default: []

    Enable undo on delete to reverse autoformatting.

    • Default: false

Predefined Rules

You can import the following predefined rule sets:

NameDescription
autoformatSmartQuotesConverts "text" to "text".
Converts 'text' to 'text'.
autoformatPunctuationConverts -- to .
Converts ... to .
Converts >> to ».
Converts << to «.
autoformatArrowConverts -> to .
Converts <- to .
Converts => to .
Converts <= and ≤= to .
autoformatLegalConverts (tm) and (TM) to .
Converts (r) and (R) to ®.
Converts (c) and (C) to ©.
autoformatLegalHtmlConverts &trade; to .
Converts &reg; to ®.
Converts &copy; to ©.
Converts &sect; to §.
autoformatComparisonConverts !> to .
Converts !< to .
Converts >= to .
Converts <= to .
Converts !>= to .
Converts !<= to .
autoformatEqualityConverts != to .
Converts == to .
Converts !== and ≠= to .
Converts ~= to .
Converts !~= to .
autoformatFractionConverts 1/2 to ½.
Converts 1/3 to .
...
Converts 7/8 to .
autoformatDivisionConverts // to ÷.
autoformatOperationConverts +- to ±.
Converts %% to .
Converts %%% and ‰% to .
autoformatDivision rules.
autoformatSubscriptNumbersConverts ~0 to .
Converts ~1 to .
...
Converts ~9 to .
autoformatSubscriptSymbolsConverts ~+ to .
Converts ~- to .
autoformatSuperscriptNumbersConverts ^0 to .
Converts ^1 to ¹.
...
Converts ^9 to .
autoformatSuperscriptSymbolsConverts ^+ to .
Converts ^- to .
autoformatMathautoformatComparison rules
autoformatEquality rules
autoformatOperation rules
autoformatFraction rules
autoformatSubscriptNumbers rules
autoformatSubscriptSymbols rules
autoformatSuperscriptNumbers rules
autoformatSuperscriptSymbols rules

Types

AutoformatCommonRule

An interface for the common structure of autoformat rules, regardless of their mode.

Attributes

Collapse all

    The rule applies when the trigger and the text just before the cursor matches.

    • For mode: 'block': lookup for the end match(es) before the cursor.
    • For mode: 'text': lookup for the end match(es) before the cursor. If format is an array, also lookup for the start match(es).
    • For mode: 'mark': lookup for the start and end matches.
    • Note: '_*', ['_*'] and { start: '_*', end: '*_' } are equivalent.
    • MatchRange:

    Triggering character to autoformat.

    If true, insert the triggering character after autoformatting.

    • Default: false

    A query function to allow autoformat.

    OptionsAutoformatQueryOptions

    Collapse all

      insertText text.

AutoformatBlockRule

An interface for autoformat rules for block mode.

Attributes

Collapse all

    Block mode: set block type or custom format.

    Pattern to match for the autoformat rule.

    For mode: 'block': set block type. If format is defined, this field is ignored.

    Whether trigger should be at block start.

    • Default: true

    Whether to allow autoformat with same block type above.

    • Default: false

    Function called before format. Used to reset selected block.

    Custom formatting function.

AutoformatMarkRule

An interface for autoformat rules for mark mode.

Attributes

Collapse all

    Mark mode: insert mark(s) between matches.

    Mark(s) to add.

    Whether to format when string can be trimmed.

AutoformatTextRule

An interface for autoformat rules for text mode.

Parameters

Collapse all

    Text mode: insert text.

    Pattern to match for the autoformat rule.

    Text replacement or formatting function.