Autoformat

Markdown shortcuts and text substitutions powered by input rules.

For the full runtime — dispatch, ownership, selection context, and custom rule authoring — see the Plugin Input Rules guide. This page is the kit-first entry for markdown shortcuts and text substitutions.

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 { createValue } from './values/demo-values';

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

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

Features

  • Markdown shortcuts owned by the feature plugins that understand them.
  • Text substitutions powered by local createTextSubstitutionInputRule(...) definitions.
  • Explicit activation through inputRules.
  • No hidden default shortcut behavior.

Kit Usage

Installation

The fastest way to add common text substitutions is with AutoformatKit.

'use client';
 
import type { SlateEditor } from 'platejs';
 
import {
  createSlatePlugin,
  createTextSubstitutionInputRule,
  KEYS,
} from 'platejs';
 
const isTextSubstitutionBlocked = (editor: SlateEditor) =>
  editor.api.some({
    match: {
      type: [editor.getType(KEYS.codeBlock)],
    },
  });
 
const createAutoformatTextSubstitutionRule = ({
  patterns,
}: {
  patterns: Parameters<typeof createTextSubstitutionInputRule>[0]['patterns'];
}) =>
  createTextSubstitutionInputRule({
    enabled: ({ editor }) => !isTextSubstitutionBlocked(editor),
    patterns,
  });
 
const arrowsRule = createAutoformatTextSubstitutionRule({
  patterns: [
    { format: '→', match: '->' },
    { format: '←', match: '<-' },
    { format: '⇒', match: '=>' },
    { format: '⇐', match: ['<=', '≤='] },
  ],
});
 
const comparisonsRule = createAutoformatTextSubstitutionRule({
  patterns: [
    { format: '≯', match: '!>' },
    { format: '≮', match: '!<' },
    { format: '≥', match: '>=' },
    { format: '≤', match: '<=' },
    { format: '≱', match: '!>=' },
    { format: '≰', match: '!<=' },
  ],
});
 
const equalityRule = createAutoformatTextSubstitutionRule({
  patterns: [
    { format: '≠', match: '!=' },
    { format: '≡', match: '==' },
    { format: '≢', match: ['!==', '≠='] },
    { format: '≈', match: '~=' },
    { format: '≉', match: '!~=' },
  ],
});
 
const fractionsRule = createAutoformatTextSubstitutionRule({
  patterns: [
    { format: '½', match: '1/2' },
    { format: '⅓', match: '1/3' },
    { format: '¼', match: '1/4' },
    { format: '⅕', match: '1/5' },
    { format: '⅙', match: '1/6' },
    { format: '⅐', match: '1/7' },
    { format: '⅛', match: '1/8' },
    { format: '⅑', match: '1/9' },
    { format: '⅒', match: '1/10' },
    { format: '⅔', match: '2/3' },
    { format: '⅖', match: '2/5' },
    { format: '¾', match: '3/4' },
    { format: '⅗', match: '3/5' },
    { format: '⅜', match: '3/8' },
    { format: '⅘', match: '4/5' },
    { format: '⅚', match: '5/6' },
    { format: '⅝', match: '5/8' },
    { format: '⅞', match: '7/8' },
  ],
});
 
const legalRule = createAutoformatTextSubstitutionRule({
  patterns: [
    { format: '™', match: ['(tm)', '(TM)'] },
    { format: '®', match: ['(r)', '(R)'] },
    { format: '©', match: ['(c)', '(C)'] },
  ],
});
 
const legalHtmlRule = createAutoformatTextSubstitutionRule({
  patterns: [
    { format: '™', match: '&trade;' },
    { format: '®', match: '&reg;' },
    { format: '©', match: '&copy;' },
    { format: '§', match: '&sect;' },
  ],
});
 
const operatorsRule = createAutoformatTextSubstitutionRule({
  patterns: [
    { format: '±', match: '+-' },
    { format: '‰', match: '%%' },
    { format: '‱', match: ['%%%', '‰%'] },
  ],
});
 
const punctuationRule = createAutoformatTextSubstitutionRule({
  patterns: [
    { format: '»', match: '>>' },
    { format: '«', match: '<<' },
  ],
});
 
const smartQuotesRule = createAutoformatTextSubstitutionRule({
  patterns: [
    { format: ['“', '”'], match: '"' },
    { format: ['‘', '’'], match: "'" },
  ],
});
 
const subscriptNumbersRule = createAutoformatTextSubstitutionRule({
  patterns: [
    { format: '₀', match: '~0' },
    { format: '₁', match: '~1' },
    { format: '₂', match: '~2' },
    { format: '₃', match: '~3' },
    { format: '₄', match: '~4' },
    { format: '₅', match: '~5' },
    { format: '₆', match: '~6' },
    { format: '₇', match: '~7' },
    { format: '₈', match: '~8' },
    { format: '₉', match: '~9' },
  ],
});
 
const subscriptSymbolsRule = createAutoformatTextSubstitutionRule({
  patterns: [
    { format: '₊', match: '~+' },
    { format: '₋', match: '~-' },
  ],
});
 
const superscriptNumbersRule = createAutoformatTextSubstitutionRule({
  patterns: [
    { format: '⁰', match: '^0' },
    { format: '¹', match: '^1' },
    { format: '²', match: '^2' },
    { format: '³', match: '^3' },
    { format: '⁴', match: '^4' },
    { format: '⁵', match: '^5' },
    { format: '⁶', match: '^6' },
    { format: '⁷', match: '^7' },
    { format: '⁸', match: '^8' },
    { format: '⁹', match: '^9' },
  ],
});
 
const superscriptSymbolsRule = createAutoformatTextSubstitutionRule({
  patterns: [
    { format: '°', match: '^o' },
    { format: '⁺', match: '^+' },
    { format: '⁻', match: '^-' },
  ],
});
 
const AutoformatShortcutsPlugin = createSlatePlugin({
  key: 'autoformatShortcuts',
  inputRules: [
    legalRule,
    legalHtmlRule,
    arrowsRule,
    comparisonsRule,
    equalityRule,
    fractionsRule,
    operatorsRule,
    punctuationRule,
    smartQuotesRule,
    subscriptNumbersRule,
    subscriptSymbolsRule,
    superscriptNumbersRule,
    superscriptSymbolsRule,
  ],
});
 
export const AutoformatKit = [AutoformatShortcutsPlugin];

Add Kit

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

Manual Usage

Feature-Owned Markdown Shortcuts

Markdown shortcuts live on the plugins that own those features:

import {
  BlockquoteRules,
  HeadingRules,
  HorizontalRuleRules,
} from '@platejs/basic-nodes';
import {
  BlockquotePlugin,
  H1Plugin,
  HorizontalRulePlugin,
} from '@platejs/basic-nodes/react';
import { CodeBlockRules } from '@platejs/code-block';
import { CodeBlockPlugin } from '@platejs/code-block/react';
import { LinkRules } from '@platejs/link';
import { LinkPlugin } from '@platejs/link/react';
import {
  BulletedListRules,
  OrderedListRules,
  TaskListRules,
} from '@platejs/list';
import { ListPlugin } from '@platejs/list/react';
import { MathRules } from '@platejs/math';
import { EquationPlugin, InlineEquationPlugin } from '@platejs/math/react';
 
const editor = createPlateEditor({
  plugins: [
    H1Plugin.configure({
      inputRules: [HeadingRules.markdown()],
    }),
    BlockquotePlugin.configure({
      inputRules: [BlockquoteRules.markdown()],
    }),
    HorizontalRulePlugin.configure({
      inputRules: [HorizontalRuleRules.markdown({ variant: '-' })],
    }),
    CodeBlockPlugin.configure({
      inputRules: [CodeBlockRules.markdown({ on: 'match' })],
    }),
    ListPlugin.configure({
      inputRules: [
        BulletedListRules.markdown({ variant: '-' }),
        OrderedListRules.markdown({ variant: '.' }),
        TaskListRules.markdown({ checked: false }),
      ],
    }),
    InlineEquationPlugin.configure({
      inputRules: [MathRules.markdown({ variant: '$' })],
    }),
    EquationPlugin.configure({
      inputRules: [MathRules.markdown({ on: 'break', variant: '$$' })],
    }),
    LinkPlugin.configure({
      inputRules: [
        LinkRules.markdown(),
        LinkRules.autolink({ variant: 'paste' }),
        LinkRules.autolink({ variant: 'space' }),
        LinkRules.autolink({ variant: 'break' }),
      ],
    }),
  ],
});
  • Package rule families own the markdown semantics. Kits register explicit rule instances.

Local Text Substitutions

Generic substitutions such as smart quotes, arrows, and fractions are best kept as local copied plugin code:

import {
  createSlatePlugin,
  createTextSubstitutionInputRule,
} from 'platejs';
 
const ShortcutsPlugin = createSlatePlugin({
  key: 'shortcuts',
  inputRules: [
    createTextSubstitutionInputRule({
      patterns: [{ format: '—', match: '--' }],
    }),
  ],
});
 
const editor = createPlateEditor({
  plugins: [ShortcutsPlugin],
});

Fine-Grained Overrides

Rule names are the override unit:

import { ItalicRules } from '@platejs/basic-nodes';
 
ItalicPlugin.configure({
  inputRules: [ItalicRules.markdown({ variant: '_' })],
});