@platejs/autoformat@53.0.0

udecode/plate@platejs/autoformat@53.0.0Apr 23, 2026by github-actions[bot]

AI Summary

Major deprecation of the autoformat package; markdown shortcuts moved to inputRules on feature plugins.

Key Highlights

  • Deprecate @platejs/autoformat package
  • Markdown shortcuts and text substitutions now authored as inputRules on feature plugins
  • AutoformatPlugin becomes an inert compatibility export
  • Migration guide provided mapping old rules to new rule factories (HeadingRules, BoldRules, etc.)

Breaking Changes

  • AutoformatPlugin is deprecated and must be removed
  • Autoformat rules configuration structure changed to inputRules on owning plugins
  • enableUndoOnDelete option removed
  • rules[].query replaced by enabled on rule factory

Full Release Notes

### Major Changes

-   [#4941](https://github.com/udecode/plate/pull/4941) by [@zbeyens](https://github.com/zbeyens) – Deprecate `@platejs/autoformat`. Markdown shortcuts and text substitutions are now authored as `inputRules` on each feature plugin, and `AutoformatPlugin` remains only as an inert compatibility export.

    **Migration:**

    1.  Remove `AutoformatPlugin` from your plugins and replace `@platejs/autoformat` after migrating rules.
    2.  Replace each old `AutoformatRule` with the matching rule factory on the plugin that owns the feature. See the table below.
    3.  Replace symbol substitutions (arrows, fractions, smart quotes, legal, math operators) with `createTextSubstitutionInputRule` registered on a local `createSlatePlugin`.
    4.  Replace `rules[].query` with `enabled` on the rule factory call. Replace the global code-block guard with a per-plugin `enabled` check.
    5.  Drop `enableUndoOnDelete` — undo-on-delete is the built-in behavior.
    6.  Replace custom `AutoformatRule` definitions with `createRuleFactory` from `platejs`.

    ```tsx
    // Before
    import { AutoformatPlugin } from "@platejs/autoformat";

    const editor = createPlateEditor({
      plugins: [
        AutoformatPlugin.configure({
          options: {
            enableUndoOnDelete: true,
            rules: [
              { match: "# ", mode: "block", type: KEYS.h1 },
              { match: "**", mode: "mark", type: KEYS.bold },
              {
                match: "* ",
                mode: "block",
                type: "list",
                format: (editor) =>
                  toggleList(editor, { listStyleType: KEYS.ul }),
              },
            ],
          },
        }),
      ],
    });

    // After
    import { BoldRules } from "@platejs/basic-nodes";
    import { BoldPlugin } from "@platejs/basic-nodes/react";
    import { HeadingRules } from "@platejs/basic-nodes";
    import { H1Plugin } from "@platejs/basic-nodes/react";
    import { BulletedListRules } from "@platejs/list";
    import { ListPlugin } from "@platejs/list/react";

    const editor = createPlateEditor({
      plugins: [
        H1Plugin.configure({ inputRules: [HeadingRules.markdown()] }),
        BoldPlugin.configure({
          inputRules: [BoldRules.markdown({ variant: "*" })],
        }),
        ListPlugin.configure({
          inputRules: [BulletedListRules.markdown({ variant: "-" })],
        }),
      ],
    });
    ```

    ### Rule Map

    #### Basic blocks — `@platejs/basic-nodes`

    | Old rule                                                         | New rule                                                                                                                                           |
    | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `{ match: '# '..'###### ', mode: 'block', type: KEYS.h1..h6 }`   | `HxPlugin.configure({ inputRules: [HeadingRules.markdown()] })` — register on each `H1Plugin`..`H6Plugin`                                          |
    | `{ match: '> ', mode: 'block', type: KEYS.blockquote }`          | `BlockquotePlugin.configure({ inputRules: [BlockquoteRules.markdown()] })`                                                                         |
    | `{ match: ['---', '—-', '___ '], mode: 'block', type: KEYS.hr }` | `HorizontalRulePlugin.configure({ inputRules: [HorizontalRuleRules.markdown({ variant: '-' }), HorizontalRuleRules.markdown({ variant: '_' })] })` |

    #### Basic marks — `@platejs/basic-nodes`

    | Old rule                                                             | New rule                                                      | Owning plugin         |
    | -------------------------------------------------------------------- | ------------------------------------------------------------- | --------------------- |
    | `{ match: '**', mode: 'mark', type: KEYS.bold }`                     | `BoldRules.markdown({ variant: '*' })`                        | `BoldPlugin`          |
    | `{ match: '__', mode: 'mark', type: KEYS.underline }`                | `UnderlineRules.markdown()`                                   | `UnderlinePlugin`     |
    | `{ match: '*', mode: 'mark', type: KEYS.italic }`                    | `ItalicRules.markdown({ variant: '*' })`                      | `ItalicPlugin`        |
    | `{ match: '_', mode: 'mark', type: KEYS.italic }`                    | `ItalicRules.markdown({ variant: '_' })`                      | `ItalicPlugin`        |
    | ``{ match: '`', mode: 'mark', type: KEYS.code }``                    | `CodeRules.markdown()`                                        | `CodePlugin`          |
    | `{ match: '~~', mode: 'mark', type: KEYS.strikethrough }`            | `StrikethroughRules.markdown()`                               | `StrikethroughPlugin` |
    | `{ match: '~', mode: 'mark', type: KEYS.sub }`                       | `SubscriptRules.markdown()`                                   | `SubscriptPlugin`     |
    | `{ match: '^', mode: 'mark', type: KEYS.sup }`                       | `SuperscriptRules.markdown()`                                 | `SuperscriptPlugin`   |
    | `{ match: '==', mode: 'mark', type: KEYS.highlight }`                | `HighlightRules.markdown({ variant: '==' })`                  | `HighlightPlugin`     |
    | `{ match: '≡', mode: 'mark', type: KEYS.highlight }`                 | `HighlightRules.markdown({ variant: '≡' })`                   | `HighlightPlugin`     |
    | `{ match: '***', mode: 'mark', type: [bold, italic] }`               | `MarkComboRules.markdown({ variant: 'boldItalic' })`          | `BoldPlugin`          |
    | `{ match: '__*', mode: 'mark', type: [underline, italic] }`          | `MarkComboRules.markdown({ variant: 'italicUnderline' })`     | `BoldPlugin`          |
    | `{ match: '__**', mode: 'mark', type: [underline, bold] }`           | `MarkComboRules.markdown({ variant: 'boldUnderline' })`       | `BoldPlugin`          |
    | `{ match: '___***', mode: 'mark', type: [underline, bold, italic] }` | `MarkComboRules.markdown({ variant: 'boldItalicUnderline' })` | `BoldPlugin`          |

    Register each family on its owning plugin:

    ```tsx
    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" }),
      ],
    });
    ```

    #### Code block — `@platejs/code-block`

    | Old rule                                                                                    | New rule                                                                                |
    | ------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
    | ````{ match: '```', mode: 'block', type: KEYS.codeBlock, format: insertEmptyCodeBlock }```` | `CodeBlockPlugin.configure({ inputRules: [CodeBlockRules.markdown({ on: 'match' })] })` |

    #### Lists — `@platejs/list` and `@platejs/list-classic`

    | Old rule                                                                                                   | New rule                                                                                       |
    | ---------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
    | `{ match: ['- ', '* '], mode: 'block', format: toggleList(..., { listStyleType: KEYS.ul }) }`              | `BulletedListRules.markdown({ variant: '-' })`, `BulletedListRules.markdown({ variant: '*' })` |
    | `{ match: /^\d+\.$ \|^\d+\)$ /, matchByRegex: true, format: toggleList(..., { listStyleType: KEYS.ol }) }` | `OrderedListRules.markdown({ variant: '.' })`, `OrderedListRules.markdown({ variant: ')' })`   |
    | `{ match: '[] ', mode: 'block', format: toggleList(..., { listStyleType: KEYS.listTodo }) }`               | `TaskListRules.markdown({ checked: false })`                                                   |
    | `{ match: '[x] ', mode: 'block', format: toggleList + setNodes({ checked: true }) }`                       | `TaskListRules.markdown({ checked: true })`                                                    |

    ```tsx
    ListPlugin.configure({
      inputRules: [
        BulletedListRules.markdown({ variant: "-" }),
        BulletedListRules.markdown({ variant: "*" }),
        OrderedListRules.markdown({ variant: "." }),
        OrderedListRules.markdown({ variant: ")" }),
        TaskListRules.markdown({ checked: false }),
        TaskListRules.markdown({ checked: true }),
      ],
    });
    ```

    Replace `@platejs/list` with `@platejs/list-classic` imports when using the classic list model. The factory names are identical.

    #### Math — `@platejs/math`

    | Old rule               | New rule                                                                                         |
    | ---------------------- | ------------------------------------------------------------------------------------------------ |
    | Inline equation `$…$`  | `InlineEquationPlugin.configure({ inputRules: [MathRules.markdown({ variant: '$' })] })`         |
    | Block equation `$$…$$` | `EquationPlugin.configure({ inputRules: [MathRules.markdown({ on: 'break', variant: '$$' })] })` |

    #### Link — `@platejs/link`

    | Old behavior           | New rule                                   |
    | ---------------------- | ------------------------------------------ |
    | `[text](url)` markdown | `LinkRules.markdown()`                     |
    | Autolink on paste      | `LinkRules.autolink({ variant: 'paste' })` |
    | Autolink on space      | `LinkRules.autolink({ variant: 'space' })` |
    | Autolink on Enter      | `LinkRules.autolink({ variant: 'break' })` |

    ```tsx
    LinkPlugin.configure({
      inputRules: [
        LinkRules.markdown(),
        LinkRules.autolink({ variant: "paste" }),
        LinkRules.autolink({ variant: "space" }),
        LinkRules.autolink({ variant: "break" }),
      ],
    });
    ```

    #### Text substitutions (arrows, fractions, legal, math operators, smart quotes)

    Move these to a local `createSlatePlugin` with `createTextSubstitutionInputRule`:

    ```tsx
    import {
      createSlatePlugin,
      createTextSubstitutionInputRule,
      KEYS,
    } from "platejs";

    const isTextSubstitutionBlocked = (editor) =>
      editor.api.some({ match: { type: [editor.getType(KEYS.codeBlock)] } });

    const ShortcutsPlugin = createSlatePlugin({
      key: "shortcuts",
      inputRules: [
        createTextSubstitutionInputRule({
          enabled: ({ editor }) => !isTextSubstitutionBlocked(editor),
          patterns: [
            { format: "→", match: "->" },
            { format: "⇒", match: "=>" },
            { format: "½", match: "1/2" },
            { format: "™", match: ["(tm)", "(TM)"] },
            { format: ["“", "”"], match: '"' },
          ],
        }),
      ],
    });
    ```

    Each pattern set is just data — `autoformatArrow`, `autoformatLegal`, `autoformatMath`, `autoformatPunctuation`, `autoformatSmartQuotes`, and `autoformatLegalHtml` from the old package map 1:1 onto `patterns` arrays. `AutoformatKit` in the Plate registry is pre-built with all of them.

    #### Custom rules

    Old `AutoformatRule` objects have no direct replacement. Build a rule family with `createRuleFactory`:

    ```tsx
    import { createRuleFactory } from "platejs";

    const MyRules = {
      markdown: createRuleFactory({
        type: "blockMatch",
        match: "!! ",
        format: "my-block",
      }),
    };

    MyPlugin.configure({ inputRules: [MyRules.markdown()] });
    ```

    ### Option removals

    -   `enableUndoOnDelete` — removed. Backspace on a rule-inserted node restores the source text by default.
    -   `rules[].query` — replaced by `enabled` on the rule factory call.
    -   `rules[].preFormat` / `rules[].format` — replaced by rule-family `format` and `resolve` callbacks inside `createRuleFactory`.
    -   `rules[].trigger` — rule families set their own trigger. Override it with the `trigger` option on a custom `createRuleFactory` call.

    See the [Autoformat](/docs/autoformat) doc for the kit path and the [Plugin Input Rules](/docs/plugin-input-rules) guide for the full runtime.