v49 Releases

RSS
v49.2.8

@platejs/core

Bug Fixes

  • Added onNodeChange and onTextChange callbacks to track editor operations: (#4549)

    text
    1 - `onNodeChange`: Called for node operations (insert, remove, set, merge, split, move) 2 - `onTextChange`: Called for text operations (insert, remove) 3 4 ```tsx 5 // Usage via Plate component 6 <Plate 7 onNodeChange={({ editor, node, operation, prevNode }) => { 8 console.log('Node changed:', { node, operation, prevNode }); 9 }} 10 onTextChange={({ editor, node, operation, prevText, text }) => { 11 console.log('Text changed:', { text, prevText, operation }); 12 }} 13 />; 14 15 // Usage via plugin 16 MyPlugin.configure({ 17 handlers: { 18 onNodeChange: ({ node, operation, prevNode }) => { 19 // Handle node changes 20 }, 21 onTextChange: ({ node, operation, prevText, text }) => { 22 // Handle text changes 23 }, 24 }, 25 }); 26 ```

CHANGELOG · v49.2.7...v49.2.8 · By @zbeyens

v49.2.4

@platejs/core

Bug Fixes

  • Added useFocusedLast hook to track the last focused editor (#4537)
  • Updated EventEditorPlugin to track the last focused editor in EventEditorStore

@platejs/selection

Bug Fixes

  • Fixed duplicateBlockSelectionNodes by removing the nextBlock: true option (#4537)
  • Added mod+d hotkey to duplicate selected blocks
  • Added character input handling in block selection - typing a character now replaces selected blocks with a new block containing that character
  • Added support for node and mark operations (bold, italic, etc.)

@platejs/slate

Bug Fixes

  • Fixed duplicateNodes to allow undefined at (#4537)
  • Improved error handling by only checking for nodes parameter instead of both nodes and at

CHANGELOG · v49.2.3...v49.2.4 · By @zbeyens

v49.2.1

@platejs/ai

Bug Fixes

  • AI Streaming Improvements (#4518)

    @platejs/ai:

    • Fixed empty paragraph removal logic in streamInsertChunk to only remove true empty paragraphs (no text content)
    • Enhanced streaming support for tables and columns with proper chunk insertion
    • Fixed interface name typo: SteamInsertChunkOptionsStreamInsertChunkOptions
    • Improved markdown streaming with better handling of incomplete patterns

    @platejs/layout:

    • Added streaming support for columns in withColumn
    • Fixed column width calculations to handle edge cases

    @platejs/markdown:

    • Enhanced column deserialization with proper attribute parsing
    • Added support for column groups in markdown rules
    • Improved attribute parsing in customMdxDeserialize

@platejs/layout

Bug Fixes

  • AI Streaming Improvements (#4518)

    @platejs/ai:

    • Fixed empty paragraph removal logic in streamInsertChunk to only remove true empty paragraphs (no text content)
    • Enhanced streaming support for tables and columns with proper chunk insertion
    • Fixed interface name typo: SteamInsertChunkOptionsStreamInsertChunkOptions
    • Improved markdown streaming with better handling of incomplete patterns

    @platejs/layout:

    • Added streaming support for columns in withColumn
    • Fixed column width calculations to handle edge cases

    @platejs/markdown:

    • Enhanced column deserialization with proper attribute parsing
    • Added support for column groups in markdown rules
    • Improved attribute parsing in customMdxDeserialize

@platejs/markdown

Bug Fixes

  • AI Streaming Improvements (#4518)

    @platejs/ai:

    • Fixed empty paragraph removal logic in streamInsertChunk to only remove true empty paragraphs (no text content)
    • Enhanced streaming support for tables and columns with proper chunk insertion
    • Fixed interface name typo: SteamInsertChunkOptionsStreamInsertChunkOptions
    • Improved markdown streaming with better handling of incomplete patterns

    @platejs/layout:

    • Added streaming support for columns in withColumn
    • Fixed column width calculations to handle edge cases

    @platejs/markdown:

    • Enhanced column deserialization with proper attribute parsing
    • Added support for column groups in markdown rules
    • Improved attribute parsing in customMdxDeserialize

CHANGELOG · v49.2.0...v49.2.1 · By @felixfeng33

v49.2.0

@platejs/list

Features

  • Added expandListItemsWithChildren to automatically include list item children when selecting blocks for operations like drag-and-drop. (#4514)
  • Added getListChildren to get all child list items (with bigger indent) of a given list item.

CHANGELOG · v49.1.13...v49.2.0 · By @zbeyens

v49.1.13

@platejs/list

Bug Fixes

  • Fixed toggleList to respect list plugin's inject match when applying list transforms. (#4512)

@platejs/slate

Bug Fixes

  • Added combineTransformMatchOptions utility for combining match predicates in transforms. This utility provides default matching behavior that matches the native Slate transform behavior when no match is provided. (#4512)
  • Added editor.meta.isNormalizing flag to track when the editor is normalizing nodes. This flag is automatically set to true during editor.tf.normalizeNode and plugin.normalizeInitialValue and restored to its previous value when normalization completes.

@platejs/table

Bug Fixes

  • Fixed table cell selection "remove marks" and "set nodes" transforms (e.g. align, list) (#4512)

CHANGELOG · v49.1.12...v49.1.13 · By @zbeyens

v49.1.9

@platejs/markdown

Bug Fixes

  • Fixed markdown serialization of indented lists when using custom paragraph node types. The serializer now correctly identifies custom paragraph nodes instead of only looking for the default 'p' type. (#4493)

CHANGELOG · v49.1.8...v49.1.9 · By @felixfeng33

v49.1.8

@platejs/markdown

Bug Fixes

  • Fix custom plugin key handling in markdown serialization/deserialization. Ensures plugin keys are properly resolved throughout the conversion process for custom plugin configurations. (#4486)

CHANGELOG · v49.1.7...v49.1.8 · By @felixfeng33

v49.1.7

@platejs/dnd

Bug Fixes

  • Added support for dragging multiple blocks using editor's native selection (#4481)

    • Multiple blocks can now be dragged using the editor's native selection, not just with block-selection
    • Simplified useDndNode hook implementation by removing complex preview logic

CHANGELOG · v49.1.6...v49.1.7 · By @felixfeng33

v49.1.6

@platejs/markdown

Bug Fixes

  • Add support for display text markdown format for mentions (#4468)

    • Updated remarkMention plugin to only support the [display text](mention:id) format
    • Dropped support for legacy @username format
    • Mentions now require an explicit display text and ID structure
    • Enables full names, spaces, and special characters in mention display text

CHANGELOG · v49.1.5...v49.1.6 · By @felixfeng33

v49.1.4

@platejs/core

Bug Fixes

  • Fixed getSelectedDomFragment to correctly handle partial text selections at the beginning or end of selected blocks by deserializing only the selected portion instead of the entire block. (#4463)

CHANGELOG · v49.1.3...v49.1.4 · By @felixfeng33

v49.1.3

@platejs/core

Bug Fixes

  • Added editor.tf.nodeId.normalize() API to manually normalize node IDs in the document. (#4454)

    text
    1 ```ts 2 // Normalize all nodes in the document to ensure they have IDs 3 editor.tf.nodeId.normalize(); 4 ```
    • Added normalizeNodeId pure function to normalize node IDs in a value without using editor operations.

      ts
      1import { normalizeNodeId } from '@platejs/core'; 2 3// Normalize a value without editor operations 4const normalizedValue = normalizeNodeId(value, { 5 idKey: 'id', 6 idCreator: () => nanoid(10), 7 filterInline: true, 8 filterText: true, 9});

      This is useful when the value is passed from server to client-side editor.

    • Added getFragment() API method to ViewPlugin for accessing the selected DOM fragment.

    usePlateViewEditor:

    • Added onReady handler support for async rendering with automatic re-render when isAsync is true
    typescript
    1// New API usage 2const fragment = editor.getApi(ViewPlugin).getFragment(); 3 4// Async rendering support 5const editor = usePlateViewEditor({ 6 onReady: (ctx) => { 7 // Called when editor is ready, supports async rendering 8 }, 9});

CHANGELOG · v49.1.2...v49.1.3 · By @felixfeng33

v49.1.1

@platejs/link

Bug Fixes

  • Fix markdown headings being incorrectly converted to links (#4452)

    The LinkPlugin's validateUrl function now properly distinguishes between markdown headings and anchor links. Previously, any string starting with # was treated as a valid link, causing markdown headings like # heading1 to be converted to links when pasted. Now, the function checks for the markdown heading pattern (hash symbols followed by a space) and correctly rejects these as invalid URLs while still allowing valid anchor links like #section-name.

CHANGELOG · v49.1.0...v49.1.1 · By @zbeyens

v49.1.0

@platejs/list-classic

Features

  • Added task list functionality to @platejs/list-classic. (36211fa)

    • Added BaseTaskListPlugin with support for task lists (checklists)
    • Added checked property to TTodoListItemElement type for tracking task completion state
    • Added useTodoListElement and useTodoListElementState hooks for task list item management
    • Added getPropsIfTaskList utility to check if an element is a task list
    • Added normalization logic to ensure consistent checked property state
    • Added toggleTaskList transform to convert between regular lists and task lists
    tsx
    1// Before - only regular lists 2createListPlugin(); 3 4// After - with task list support 5createListPlugin(); 6 7// Toggle task list 8editor.tf.toggle.list({ listType: 'taskList' });

CHANGELOG · v49.0.19...v49.1.0 · By @zbeyens

v49.0.17

@platejs/markdown

Bug Fixes

  • Added spread option to control list spacing in markdown serialization. (#4440)

    Added a new optional spread property to SerializeMdOptions:

    • When spread is false (default), lists are rendered compactly
    • When spread is true, lists have double line breaks between items

    Before (default):

    markdown
    11. Item 1 22. Item 2

    After (with spread: true):

    markdown
    11. Item 1 2 32. Item 2

CHANGELOG · v49.0.16...v49.0.17 · By @zbeyens

v49.0.16

@platejs/core

Bug Fixes

  • Expose mimeType to plugin parser functions (#4441)

  • Added comprehensive copy functionality and view editor support for static rendering. (#4431)

    New Components:

    • Added PlateView component for static editor rendering with copy support
    • Added usePlateViewEditor hook for creating memoized static editors

    Static Editor Enhancements:

    • Added withStatic HOC to enhance editors with static rendering capabilities
    • Added ViewPlugin that enables copy operations in static editors
    • Added getStaticPlugins to configure plugins for static rendering
    • Added onCopy handler that properly serializes content with x-slate-fragment format

    New Utilities:

    • Added getSelectedDomBlocks to extract selected DOM elements with Slate metadata
    • Added getSelectedDomNode to get DOM nodes from browser selection
    • Added isSelectOutside to check if selection is outside editor bounds
    • Added getPlainText to recursively extract plain text from DOM nodes

    This enables seamless copy operations from static Plate editors, allowing content to be pasted into other Slate editors while preserving rich formatting and structure.

CHANGELOG · v49.0.15...v49.0.16 · By @delijah, @felixfeng33

v49.0.15

@platejs/core

Bug Fixes

  • Updated SlateElementProps, SlateTextProps, SlateLeafProps, PlateElementProps, PlateTextProps, and PlateLeafProps to properly type the attributes property to unknown object. (#4428)

@platejs/link

Bug Fixes

  • Improved return type of getLinkAttributes to be more specific and type-safe. (#4428)

    ts
    1// The function now returns a properly typed object 2const attributes = getLinkAttributes(editor, linkElement); 3// attributes is now properly typed as Pick<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'href' | 'target'> & UnknownObject

@platejs/selection

Bug Fixes

  • fix(selection): skip empty blocks in copySelectedBlocks to prevent duplication (#4415)

@udecode/react-utils

Bug Fixes

  • Critical fix for 49.0.13 (#4434)

CHANGELOG · v49.0.14...v49.0.15 · By @zbeyens

v49.0.12

@platejs/ai

Bug Fixes

  • Fixed AI streaming compatibility with markdown serialization changes. Streaming functions now explicitly set preserveEmptyParagraphs: false to prevent zero-width space interference during real-time streaming operations. (#4416)

@platejs/markdown

Bug Fixes

  • Fixed an issue where empty paragraphs were lost during markdown serialization and deserialization. Empty paragraphs are now preserved using zero-width spaces (\u200B) internally. (#4416)

    text
    1 ```ts 2 // Before: Empty paragraphs would disappear 3 const markdown = serializeMd(editor); // "Text\n\nMore text" → "Text\nMore text" 4 5 // After: Empty paragraphs are preserved 6 const markdown = serializeMd(editor); // "Text\n\nMore text" → "Text\n\nMore text" 7 ```
    • Added preserveEmptyParagraphs option to control this behavior (defaults to true)

CHANGELOG · v49.0.11...v49.0.12 · By @zbeyens

v49.0.11

@platejs/core

Bug Fixes

  • Fixed BR tags between block elements from Google Docs creating two empty paragraphs instead of one. The deserialization now correctly converts BR tags between blocks to single empty paragraphs. (#4411)

CHANGELOG · v49.0.10...v49.0.11 · By @zbeyens

v49.0.7

@platejs/dnd

Bug Fixes

  • Fixed an issue where drag and drop functionality would not work properly with multiple selected blocks. (#4385)
  • Added logic to ensure only one drop position exists between any two nodes to prevent visual blinking during drag operations.

@platejs/selection

Bug Fixes

  • Added sort and collapseTableRows options to editor.blockSelection.getNodes() method. (#4385)
  • Added editor.blockSelection.first to get the first selected node.
  • Added normalize function to handle table selection logic in useSelectionArea hook for improved table row and table element selection behavior.
    • It is now possible to select the entire table (table), but the rows (tr) will only be selected if your selection box is within the table.

CHANGELOG · v49.0.6...v49.0.7 · By @felixfeng33

v49.0.4

@platejs/core

Bug Fixes

  • Fixes #4374 (#4373)

  • Prevent rendering the editor until the value is loaded (when value is async or skipInitialization is true).

  • Added support for both synchronous and asynchronous functions in the value option for createPlateEditor and usePlateEditor. If async, usePlateEditor will trigger a re-render when the value is loaded.

  • Added onReady callback option to createPlateEditor and usePlateEditor called after (async) editor initialization.

    ts
    1const editor = usePlateEditor({ 2 value: async () => { 3 const response = await fetch('/api/document'); 4 const data = await response.json(); 5 return data.content; 6 }, 7 onReady: ({ editor, value }) => { 8 console.info('Editor ready with value:', value); 9 }, 10});

CHANGELOG · v49.0.3...v49.0.4 · By @zbeyens

v49.0.0

@udecode/plate-ai

Breaking Changes

  • Copilot API method changes: (#4327)

    • editor.api.copilot.accept is now editor.tf.copilot.accept.
    • editor.api.copilot.acceptNextWord is now editor.tf.copilot.acceptNextWord.
    • editor.api.copilot.reset is now editor.api.copilot.reject.
  • Removed Default Shortcuts for Copilot:

    • Only accept (Tab) and reject (Escape) shortcuts are included by default for CopilotPlugin.
    • acceptNextWord and triggerSuggestion shortcuts must now be configured manually using the shortcuts field when configuring the plugin.
    • Example:
      tsx
      1CopilotPlugin.configure({ 2 // ... other options 3 shortcuts: { 4 acceptNextWord: { 5 keys: 'mod+right', 6 }, 7 triggerSuggestion: { 8 keys: 'ctrl+space', 9 }, 10 }, 11});
  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-alignment

Breaking Changes

  • Package @udecode/plate-alignment has been deprecated. (#4327)

    • TextAlignPlugin (formerly AlignPlugin) has been moved to the @platejs/basic-styles package.

    • Migration:

      • Remove @udecode/plate-alignment from your dependencies.
      • Add @platejs/basic-styles to your dependencies if not already present.
      • Import TextAlignPlugin from @platejs/basic-styles/react.
    • Renamed AlignPlugin to TextAlignPlugin and changed plugin key from 'align' to 'textAlign'.

      ts
      1// Before 2import { AlignPlugin } from '@udecode/plate-alignment/react'; 3 4// After 5import { TextAlignPlugin } from '@platejs/basic-styles/react';
    • setAlign signature change:

    ts
    1// Before 2setAlign(editor, { value: 'center', setNodesOptions }); 3 4// After 5setAlign(editor, 'center', setNodesOptions);
    • Removed useAlignDropdownMenu and useAlignDropdownMenuState. Use it in your own codebase, for example:
    tsx
    1export function AlignToolbarButton() { 2 const editor = useEditorRef(); 3 const value = useSelectionFragmentProp({ 4 defaultValue: 'start', 5 structuralTypes, 6 getProp: (node) => node.align, 7 }); 8 9 const onValueChange = (newValue: string) => { 10 editor.tf.textAlign.setNodes(newValue as Alignment); 11 editor.tf.focus(); 12 }; 13 14 // ... 15}

Features

  • New transform method to AlignPlugin: (#4327)
    • editor.tf.textAlign.setNodes - Transform method for setting alignment values. Alias to setAlign

@udecode/plate-autoformat

Breaking Changes

  • Replaced BaseAutoformatPlugin with AutoformatPlugin, which is no longer a React plugin. Migration: Replace @udecode/plate-autoformat/react import with @udecode/plate-autoformat. (#4327)

  • The following plugins now default to editOnly: true. This means their core functionalities (handlers, rendering injections, etc.) will be disabled when the editor is in read-only mode. To override this behavior for a specific plugin, configure its editOnly field. For example, SomePlugin.configure({ editOnly: false }). (#4327)

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-basic-elements

Breaking Changes

  • Package @udecode/plate-basic-elements has been deprecated. (#4327)
  • BasicElementsPlugin has been renamed to BasicBlocksPlugin.
  • Its plugins have been moved to the new @platejs/basic-nodes package.
  • Migration:
    • Replace @udecode/plate-basic-elements with @platejs/basic-nodes in your dependencies.
    • Update import paths from @udecode/plate-basic-elements/react to @platejs/basic-nodes/react.
    • For detailed changes to individual plugins, default HTML tags, and shortcut configurations, refer to the changeset for @platejs/basic-nodes.

@udecode/plate-basic-marks

Breaking Changes

  • Package @udecode/plate-basic-marks has been deprecated. (#4327)
  • Its plugins have been moved to the new @platejs/basic-nodes package.
  • Migration:
    • Replace @udecode/plate-basic-marks with @platejs/basic-nodes in your dependencies.
    • Update import paths from @udecode/plate-basic-marks/react to @platejs/basic-nodes/react.
    • For detailed changes to individual plugins, default HTML tags, and shortcut configurations, refer to the changeset for @platejs/basic-nodes.

@udecode/plate-basic-nodes

Breaking Changes

  • The packages @udecode/plate-basic-elements and @udecode/plate-basic-marks have been deprecated. All their plugins are now consolidated into the new @platejs/basic-nodes package. (#4327)
  • Migration:
    • Replace @udecode/plate-basic-elements and @udecode/plate-basic-marks in your dependencies with @platejs/basic-nodes.
    • Update all import paths from @udecode/plate-basic-elements/react or @udecode/plate-basic-marks/react to @platejs/basic-nodes/react.
    • CodeBlockPlugin is not part of @platejs/basic-nodes. Ensure it is imported from @platejs/code-block/react.
  • SkipMarkPlugin (standalone) is removed. Its functionality is now built into the core editor. To enable boundary clearing for a specific mark, configure the mark plugin directly: plugin.configure({ rules: { selection: { affinity: 'outward' } } }).
  • Default HTML Tag Changes:
    • Blocks: Element plugins in @udecode/plate-basic-nodes (e.g., BlockquotePlugin, HeadingPlugin, HorizontalRulePlugin) now default to rendering with specific HTML tags (<blockquote>, <h1>-<h6>, <hr> respectively). ParagraphPlugin still defaults to <div>. If you relied on previous defaults or need different tags, provide a custom component or use the render.as option.
    • Marks: Mark plugins in @udecode/plate-basic-nodes (e.g., BoldPlugin, CodePlugin, ItalicPlugin) now default to specific HTML tags (<strong>, <code>, <em> respectively). If you relied on previous defaults or need different tags, provide a custom component or use the render.as option.
  • Removed Default Shortcuts:
    • Default keyboard shortcuts are no longer bundled with most plugins (exceptions: bold, italic, underline).
    • Configure shortcuts manually via the shortcuts field in plugin configuration.
    • Example (Block Plugins):
      ts
      1H1Plugin.configure({ shortcuts: { toggle: { keys: 'mod+alt+1' } } }); 2BlockquotePlugin.configure({ 3 shortcuts: { toggle: { keys: 'mod+shift+period' } }, 4});
    • Example (Mark Plugins):
      ts
      1CodePlugin.configure({ shortcuts: { toggle: { keys: 'mod+e' } } }); 2StrikethroughPlugin.configure({ 3 shortcuts: { toggle: { keys: 'mod+shift+x' } }, 4}); 5SubscriptPlugin.configure({ 6 shortcuts: { toggle: { keys: 'mod+comma' } }, 7}); 8SuperscriptPlugin.configure({ 9 shortcuts: { toggle: { keys: 'mod+period' } }, 10}); 11HighlightPlugin.configure({ 12 shortcuts: { toggle: { keys: 'mod+shift+h' } }, 13});

Features

  • New toggle Transforms Added: (#4327)
    • Block plugins with new toggle transforms: BlockquotePlugin, H1Plugin, H2Plugin, H3Plugin, H4Plugin, H5Plugin, H6Plugin.
    • All mark plugins in this package now also feature a toggle transform, including: BoldPlugin, ItalicPlugin, UnderlinePlugin, CodePlugin, StrikethroughPlugin, SubscriptPlugin, SuperscriptPlugin, KbdPlugin, HighlightPlugin.
  • Individual Heading Plugins Available:
    • H1Plugin, H2Plugin, H3Plugin, H4Plugin, H5Plugin, and H6Plugin offer a flexible alternative to the general HeadingPlugin, allowing granular control over heading level inclusion and configuration (e.g., custom components, shortcuts per level).
  • Plugin Consolidations into @udecode/plate-basic-nodes:
    • KbdPlugin (formerly from @udecode/plate-kbd).
    • HighlightPlugin (formerly from @udecode/plate-highlight).

@udecode/plate-block-quote

Breaking Changes

  • Package @udecode/plate-block-quote has been deprecated. (#4327)
  • BlockquotePlugin has been moved to the @platejs/basic-nodes package.
  • Migration:
    • Remove @udecode/plate-block-quote from your dependencies.
    • Add @platejs/basic-nodes to your dependencies if not already present.
    • Import BlockquotePlugin from @platejs/basic-nodes/react.

@udecode/plate-break

Breaking Changes

  • Package @udecode/plate-break has been deprecated. (#4327)

  • SoftBreakPlugin has been removed. Migration:

    • For shift+enter rules: no migration is needed - this behavior is built into Slate by default.
    • For enter rules: use plugin.configure({ rules: { break: { default: 'lineBreak' } } }) to insert a line break instead of a hard break on Enter keydown when the selection is within the configured node type.
    • For more complex break rules: use overrideEditor to override the insertBreak transform with custom logic.
  • ExitBreakPlugin has been moved to @platejs/utils (which is re-exported via platejs) with a simplified API and improved behavior.

    text
    1 - **Behavior Change**: Instead of always exiting to the root level of the document, exiting will now insert a block to the nearest exitable ancestor that has `isStrictSiblings: false`. This means deeply nested structures (like tables in columns) are exitable at many levels. 2 - Migration: 3 4 - Remove `@udecode/plate-break` from your dependencies. 5 - Replace `@udecode/plate-break` import with `platejs`. 6 - **Important**: If not using Plate plugins, you must set `isStrictSiblings: true` on your custom node plugins that can't have paragraph siblings for exit break to work correctly. 7 - Replace complex rule-based configuration with simple shortcuts: 8 9 ```tsx 10 // Before (old API) 11 ExitBreakPlugin.configure({ 12 options: { 13 rules: [ 14 { hotkey: 'mod+enter' }, 15 { hotkey: 'mod+shift+enter', before: true }, 16 ], 17 }, 18 }); 19 20 // After (new API) 21 ExitBreakPlugin.configure({ 22 shortcuts: { 23 insert: { keys: 'mod+enter' }, 24 insertBefore: { keys: 'mod+shift+enter' }, 25 }, 26 }); 27 ```

@udecode/plate-callout

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-caption

Breaking Changes

  • CaptionPlugin option options.plugins (accepting an array of PlatePlugin) has been renamed to options.query.allow (accepting an array of plugin keys). (#4327)

  • Migration:

    text
    1 ```tsx 2 // Before 3 CaptionPlugin.configure({ 4 options: { 5 plugins: [ImagePlugin], // ImagePlugin is an example 6 }, 7 }); 8 9 // After 10 CaptionPlugin.configure({ 11 options: { 12 query: { 13 allow: [ImagePlugin.key], // Use the plugin's key 14 }, 15 }, 16 }); 17 ```
  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-code-block

Breaking Changes

  • CodeBlockPlugin now defaults to rendering the code block container with a <pre> HTML tag if no custom component is provided for CodeBlockElement (or the plugin key code_block). (#4327)

@udecode/plate-combobox

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-comments

Breaking Changes

  • CommentsPlugin has been renamed to CommentPlugin. (#4327)

  • Update imports and plugin configurations accordingly.

    • Example: CommentsPlugin.key becomes CommentPlugin.key.
  • Package name has been changed to @platejs/comment.

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-core

Breaking Changes

  • editor.getType() now takes a pluginKey: string instead of a plugin: PlatePlugin instance. (#4327)

    • Example: Use editor.getType(ParagraphPlugin.key) instead of editor.getType(ParagraphPlugin).
  • Plugins without a key property will not be registered into the editor.

  • Passing disabled: true prop to PlateContent will now also set the editor to readOnly: true state internally.

  • Editor DOM state properties have been moved under editor.dom namespace:

    • editor.currentKeyboardEvent is now editor.dom.currentKeyboardEvent.
    • editor.prevSelection is now editor.dom.prevSelection.
  • Editor metadata properties have been moved under editor.meta namespace:

    • editor.isFallback is now editor.meta.isFallback
    • editor.key is now editor.meta.key
    • editor.pluginList is now editor.meta.pluginList
    • editor.shortcuts is now editor.meta.shortcuts
    • editor.uid is now editor.meta.uid
  • NodeIdPlugin is now enabled by default as part of the core plugins. This automatically assigns unique IDs to block nodes.

    • Migration: If you were not previously using NodeIdPlugin and wish to maintain the old behavior (no automatic IDs), explicitly disable it in your editor configuration:
      ts
      1const editor = usePlateEditor({ 2 // ...other options 3 nodeId: false, // Disables automatic node ID generation 4});
  • The components prop has been removed from serializeHtml and PlateStatic.

    • Migration: Pass the components to createSlateEditor({ components }) or the individual plugins instead.
  • Plugin Shortcuts System Changes:

    • Shortcut keys defined in editor.shortcuts are now namespaced by the plugin key (e.g., code.toggle for CodePlugin).
    • The priority property for shortcuts is used to resolve conflicts when multiple shortcuts share the exact same key combination, not for overriding shortcuts by name.
    • preventDefault for plugin shortcuts now defaults to true, unless the handler returns false (i.e. not handled). This means browser default actions for these key combinations will be prevented unless explicitly allowed.
      • Migration: If you need to allow browser default behavior for a specific shortcut, set preventDefault: false in its configuration:
        ts
        1MyPlugin.configure({ 2 shortcuts: { 3 myAction: { 4 keys: 'mod+s', 5 preventDefault: false, // Example: Allow browser's default save dialog 6 }, 7 }, 8});
  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

Features

  • New editor DOM state fields available under editor.dom: (#4327)
    • editor.dom.composing: Boolean, true if the editor is currently composing text (e.g., during IME input).
    • editor.dom.focused: Boolean, true if the editor currently has focus.
    • editor.dom.readOnly: Boolean, true if the editor is in read-only mode. Passing the readOnly prop to PlateContent will sync its value to this state and to the useEditorReadOnly hook.
  • New editor metadata fields:
    • editor.meta.components - stores the plugin components by key
  • New hook useEditorComposing: Allows subscription to the editor's composing state (editor.dom.composing) outside of PlateContent.
  • createPlateEditor and usePlateEditor now accept a readOnly option to initialize the editor in a read-only state. For dynamic read-only changes after initialization, continue to use the readOnly prop on the <Plate> or <PlateContent> component.
  • New plugin field: editOnly (boolean or object).
    • When true or when specific properties are true in the object, Plate will disable certain plugin behaviors (handlers, rendering, injections) in read-only mode and re-enable them if the editor becomes editable.
    • By default, render, handlers, and inject are considered edit-only (true). normalizeInitialValue defaults to always active (false).
    • Example: editOnly: { render: false, normalizeInitialValue: true } would make rendering active always, but normalization only in edit mode.
  • New plugin field: render.as (keyof HTMLElementTagNameMap).
    • Specifies the default HTML tag name to be used by PlateElement (default: 'div') or PlateLeaf (default: 'span') when rendering the node, but only if no custom node.component is provided for the plugin.
    • Example: render: { as: 'h1' } would make the plugin render its node as an <h1> tag by default without the need to provide a custom component.
  • New plugin field: node.isContainer (boolean).
    • When true, indicates that the plugin's elements are primarily containers for other content.
  • New plugin field: node.isStrictSiblings (boolean).
    • When true, indicates that the element enforces strict sibling type constraints and only allows specific siblings (e.g., td can only have td siblings, column can only have column siblings).
    • Used by editor.tf.insertExitBreak functionality to determine appropriate exit points in nested structures.
  • New plugin field: rules (object).
    • Configures common editing behaviors declaratively instead of overriding editor methods. See documentation for more details.
    • rules.break: Controls Enter key behavior (empty, default, emptyLineEnd, splitReset)
    • rules.delete: Controls Backspace key behavior (start, empty)
    • rules.merge: Controls block merging behavior (removeEmpty)
    • rules.normalize: Controls normalization behavior (removeEmpty)
    • rules.selection: Controls cursor positioning behavior (affinity)
    • rules.match: Conditional rule application based on node properties
  • Plugin shortcuts can now automatically leverage existing plugin transforms by specifying the transform name, in addition to custom handlers.
  • New editor transform methods for keyboard handling:
    • editor.tf.escape: Handle Escape key events. Returns true if the event is handled.
    • editor.tf.moveLine: Handle ArrowDown and ArrowUp key events with reverse option for direction. Returns true if the event is handled.
    • editor.tf.selectAll: Handle Ctrl/Cmd+A key events for selecting all content. Returns true if the event is handled.
    • editor.tf.tab: Handle Tab and Shift+Tab key events with reverse option for Shift+Tab. Returns true if the event is handled.

Bug Fixes

  • Fixed an issue where editor.api and editor.tf (transforms) were not consistently available in the props passed to default element components when no custom component was provided for a plugin. (#4327)

@udecode/plate-csv

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-cursor

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-date

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-diff

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-dnd

Breaking Changes

  • The following plugins now default to editOnly: true. This means their core functionalities (handlers, rendering injections, etc.) will be disabled when the editor is in read-only mode. To override this behavior for a specific plugin, configure its editOnly field. For example, SomePlugin.configure({ editOnly: false }). (#4327)

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-docx

Breaking Changes

  • The following plugins now default to editOnly: true. This means their core functionalities (handlers, rendering injections, etc.) will be disabled when the editor is in read-only mode. To override this behavior for a specific plugin, configure its editOnly field. For example, SomePlugin.configure({ editOnly: false }). (#4327)

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-emoji

Breaking Changes

  • The following plugins now default to editOnly: true. This means their core functionalities (handlers, rendering injections, etc.) will be disabled when the editor is in read-only mode. To override this behavior for a specific plugin, configure its editOnly field. For example, SomePlugin.configure({ editOnly: false }). (#4327)

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-excalidraw

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-find-replace

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-floating

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-font

Breaking Changes

  • Removed setBlockBackgroundColor (#4327)

  • Removed setFontSize – use tf.fontSize.addMark instead:

    ts
    1// Before 2editor.api.fontSize.addMark('16px'); 3 4// After 5editor.tf.fontSize.addMark('16px');
    • Removed useColorInput. Use it in your own codebase, for example:
    tsx
    1function ColorInput() { 2 const inputRef = React.useRef<HTMLInputElement | null>(null); 3 4 const onClick = () => { 5 inputRef.current?.click(); 6 }; 7 8 // ... 9}
    • Removed useColorsCustom and useColorsCustomState. Use it in your own codebase, for example:
    tsx
    1function ColorCustom({ color, colors, customColors, updateCustomColor }) { 2 const [customColor, setCustomColor] = React.useState<string>(); 3 const [value, setValue] = React.useState<string>(color || '#000000'); 4 5 React.useEffect(() => { 6 if ( 7 !color || 8 customColors.some((c) => c.value === color) || 9 colors.some((c) => c.value === color) 10 ) { 11 return; 12 } 13 14 setCustomColor(color); 15 }, [color, colors, customColors]); 16 17 const computedColors = React.useMemo( 18 () => 19 customColor 20 ? [ 21 ...customColors, 22 { 23 isBrightColor: false, 24 name: '', 25 value: customColor, 26 }, 27 ] 28 : customColors, 29 [customColor, customColors] 30 ); 31 32 const updateCustomColorDebounced = React.useCallback( 33 debounce(updateCustomColor, 100), 34 [updateCustomColor] 35 ); 36 37 const inputProps = { 38 value, 39 onChange: (e: React.ChangeEvent<HTMLInputElement>) => { 40 setValue(e.target.value); 41 updateCustomColorDebounced(e.target.value); 42 }, 43 }; 44 45 // ... 46}
    • Removed useColorDropdownMenu and useColorDropdownMenuState. Use it in your own codebase, for example:
    tsx
    1export function FontColorToolbarButton({ nodeType }) { 2 const editor = useEditorRef(); 3 4 const selectionDefined = useEditorSelector( 5 (editor) => !!editor.selection, 6 [] 7 ); 8 9 const color = useEditorSelector( 10 (editor) => editor.api.mark(nodeType) as string, 11 [nodeType] 12 ); 13 14 const [selectedColor, setSelectedColor] = React.useState<string>(); 15 const [open, setOpen] = React.useState(false); 16 17 const onToggle = React.useCallback( 18 (value = !open) => { 19 setOpen(value); 20 }, 21 [open, setOpen] 22 ); 23 24 const updateColor = React.useCallback( 25 (value: string) => { 26 if (editor.selection) { 27 setSelectedColor(value); 28 editor.tf.select(editor.selection); 29 editor.tf.focus(); 30 editor.tf.addMarks({ [nodeType]: value }); 31 } 32 }, 33 [editor, nodeType] 34 ); 35 36 const clearColor = React.useCallback(() => { 37 if (editor.selection) { 38 editor.tf.select(editor.selection); 39 editor.tf.focus(); 40 if (selectedColor) { 41 editor.tf.removeMarks(nodeType); 42 } 43 onToggle(); 44 } 45 }, [editor, selectedColor, onToggle, nodeType]); 46 47 React.useEffect(() => { 48 if (selectionDefined) { 49 setSelectedColor(color); 50 } 51 }, [color, selectionDefined]); 52 53 // ... 54}

@udecode/plate-heading

Breaking Changes

  • Package @udecode/plate-heading has been deprecated. (#4327)
    • HeadingPlugin and individual heading plugins (e.g., H1Plugin) have been moved to @platejs/basic-nodes.
      • Migration: Import from @platejs/basic-nodes/react (e.g., import { HeadingPlugin } from '@platejs/basic-nodes/react';).
    • TocPlugin has been moved to @platejs/toc.
      • Migration: Add @platejs/toc to your dependencies and import TocPlugin from @platejs/toc/react.
  • Remove @udecode/plate-heading from your dependencies.

@udecode/plate-highlight

Breaking Changes

  • Package @udecode/plate-highlight has been deprecated. (#4327)
  • HighlightPlugin has been moved to the @platejs/basic-nodes package.
  • Migration:
    • Remove @udecode/plate-highlight from your dependencies.
    • Add @platejs/basic-nodes to your dependencies if not already present.
    • Import HighlightPlugin from @platejs/basic-nodes/react.

@udecode/plate-horizontal-rule

Breaking Changes

  • Package @udecode/plate-horizontal-rule has been deprecated. (#4327)
  • HorizontalRulePlugin has been moved to the @platejs/basic-nodes package.
  • Migration:
    • Remove @udecode/plate-horizontal-rule from your dependencies.
    • Add @platejs/basic-nodes to your dependencies if not already present.
    • Import HorizontalRulePlugin from @platejs/basic-nodes/react.

@udecode/plate-indent

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-indent-list

Breaking Changes

  • Package @udecode/plate-indent-list has been renamed to @platejs/list. (#4327)
  • Migration:
    • Rename all import paths from @udecode/plate-indent-list to @platejs/list.
    • Update your package.json: remove @udecode/plate-indent-list and add @platejs/list.

@udecode/plate-juice

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-kbd

Breaking Changes

  • Package @udecode/plate-kbd has been deprecated. (#4327)
  • KbdPlugin has been moved to the @platejs/basic-nodes package.
  • Migration:
    • Remove @udecode/plate-kbd from your dependencies.
    • Add @platejs/basic-nodes to your dependencies if not already present.
    • Import KbdPlugin from @platejs/basic-nodes/react.

@udecode/plate-layout

Breaking Changes

  • Delete backward from a column start will merge into the previous column (#4327)

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-line-height

Breaking Changes

  • Package @udecode/plate-line-height has been deprecated. (#4327)

    • LineHeightPlugin has been moved to the @platejs/basic-styles package.

    • Migration:

      • Remove @udecode/plate-line-height from your dependencies.
      • Add @platejs/basic-styles to your dependencies if not already present.
      • Import LineHeightPlugin from @platejs/basic-styles/react.
    • setLineHeight signature change:

    ts
    1// Before 2setLineHeight(editor, { value: 1.5, setNodesOptions }); 3 4// After 5setLineHeight(editor, 1.5, setNodesOptions);
    • Removed useLineHeightDropdownMenu and useLineHeightDropdownMenuState. Use it in your own codebase, for example:
    tsx
    1export function LineHeightToolbarButton() { 2 const editor = useEditorRef(); 3 const { defaultNodeValue, validNodeValues: values = [] } = 4 editor.getInjectProps(LineHeightPlugin); 5 6 const value = useSelectionFragmentProp({ 7 defaultValue: defaultNodeValue, 8 getProp: (node) => node.lineHeight, 9 }); 10 11 const onValueChange = (newValue: string) => { 12 editor.tf.lineHeight.setNodes(Number(newValue)); 13 editor.tf.focus(); 14 }; 15 16 // ... 17}

Features

  • New transform method to LineHeightPlugin: (#4327)
    • editor.tf.lineHeight.setNodes - Transform method for setting line height values. Alias to setLineHeight

@udecode/plate-link

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-list

Breaking Changes

  • The previous @udecode/plate-list (classic list implementation) has been moved to @platejs/list-classic. (#4327)

    • Migration for users of the classic list:
      • Rename all import paths from @udecode/plate-list to @platejs/list-classic.
      • Update your package.json: remove the old @udecode/plate-list (if it was a direct dependency) and add @platejs/list-classic.
  • This package, @platejs/list, now contains the functionality previously in @udecode/plate-indent-list (indent-based list system).

    • Plugin names have been generalized: IndentListPlugin is now ListPlugin, BaseIndentListPlugin is BaseListPlugin, etc. (*IndentList* -> *List*).
    • The primary plugin key is now list (e.g., ListPlugin.key) instead of listStyleType.
    • Constants for list keys previously in INDENT_LIST_KEYS are now available under KEYS from platejs.
      • Migration for constants:
        • INDENT_LIST_KEYS.listStyleType -> KEYS.listType
        • INDENT_LIST_KEYS.todo -> KEYS.listTodo
        • INDENT_LIST_KEYS.checked -> KEYS.listChecked
        • Other INDENT_LIST_KEYS.* map to KEYS.* accordingly.
    • Removed listStyleTypes option from ListPlugin. You should control list rendering via render.belowNodes instead.
    • Removed renderListBelowNodes.
  • For changelogs of the indent-based list system (now in this package) version <=48 (when it was @udecode/plate-indent-list), refer to its archived changelog.

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-list-classic

Breaking Changes

  • Removed Default Shortcuts for BulletedListPlugin and NumberedListPlugin. (#4327)
    • These shortcuts must now be configured manually using the shortcuts field.
    • Example:
      tsx
      1BulletedListPlugin.configure({ 2 shortcuts: { toggle: { keys: 'mod+alt+5' } }, 3}); 4NumberedListPlugin.configure({ 5 shortcuts: { toggle: { keys: 'mod+alt+6' } }, 6});
  • Package @udecode/plate-list has been moved to @platejs/list-classic.
    • Migration:
      • Rename all import paths from @udecode/plate-list to @platejs/list-classic.
      • Update your package.json: remove @udecode/plate-list and add @platejs/list-classic.
  • For changelogs of @udecode/plate-list version <=48, refer to its archived changelog.

@udecode/plate-markdown

Breaking Changes

  • Function indentListToMdastTree has been renamed to listToMdastTree to align with the list plugin renames (IndentListPlugin -> ListPlugin). (#4327)

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-math

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-media

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-mention

Breaking Changes

  • The type TMentionInputElement has been removed. (#4327)

  • Use TComboboxInputElement from @udecode/plate instead for input elements, as mention functionality is built upon the combobox.

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-node-id

Breaking Changes

  • Package @udecode/plate-node-id has been deprecated. (#4327)

  • NodeIdPlugin functionality is now part of @platejs/core and is enabled by default.

  • Migration:

    text
    1 - Remove `NodeIdPlugin` from your explicit plugin list if it was added manually. 2 3 - Remove `@udecode/plate-node-id` from your dependencies. 4 5 - If you had `NodeIdPlugin` configured with options, move these options to the `nodeId` field in your main editor configuration (`createPlateEditor` or `usePlateEditor` options). 6 Example: 7 8 ```ts 9 // Before 10 // const editor = usePlateEditor({ 11 // plugins: [ 12 // NodeIdPlugin.configure({ /* ...your options... */ }), 13 // ], 14 // }); 15 16 // After 17 const editor = usePlateEditor({ 18 nodeId: { 19 /* ...your options... */ 20 }, 21 // ...other editor options 22 }); 23 ``` 24 25 - If you want to disable automatic node ID generation (to replicate behavior if you weren't using `NodeIdPlugin` before), set `nodeId: false` in your editor configuration.

@udecode/plate-normalizers

Breaking Changes

  • Package @udecode/plate-normalizers has been deprecated. (#4327)
  • Its plugins have been moved to platejs (which is re-exported via platejs).
  • Migration:
    • Remove @udecode/plate-normalizers from your dependencies.
    • Update import paths to use platejs

@udecode/plate

Breaking Changes

  • Renamed package to platejs: (#4327)
    • Replace all @udecode/plate/react with platejs/react
    • Replace all '@udecode/plate' with 'platejs'

@udecode/plate-playwright

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-reset-node

Breaking Changes

  • Package @udecode/plate-reset-node has been deprecated. Its functionality (e.g., ResetNodePlugin) is now exclusively configured using the rules.break and rules.delete options on plugin definitions. Migration: (#4327)

    • Remove @udecode/plate-reset-node from your dependencies.
    • Remove any usage of ResetNodePlugin from your project.
    • Configure reset behaviors directly on the relevant plugins by defining rules.break and/or rules.delete.

    Example: Resetting a Blockquote to a Paragraph

    typescript
    1ResetNodePlugin.configure({ 2 options: { 3 rules: [ 4 { 5 types: [BlockquotePlugin.key], 6 defaultType: ParagraphPlugin.key, 7 hotkey: 'Enter', 8 predicate: (editor) => 9 editor.api.isEmpty(editor.selection, { block: true }), 10 }, 11 ], 12 }, 13}); 14 15// After 16BlockquotePlugin.configure({ 17 rules: { 18 break: { empty: 'reset' }, 19 delete: { start: 'reset' }, 20 }, 21});

    For custom reset logic (previously onReset):

    typescript
    1// Before 2ResetNodePlugin.configure({ 3 options: { 4 rules: [ 5 { 6 types: [CodeBlockPlugin.key], 7 defaultType: ParagraphPlugin.key, 8 hotkey: 'Enter', 9 predicate: isCodeBlockEmpty, 10 onReset: unwrapCodeBlock, 11 }, 12 ], 13 }, 14}); 15 16// After 17CodeBlockPlugin.configure({ 18 rules: { 19 delete: { empty: 'reset' }, 20 }, 21}).overrideEditor(({ editor, tf: { resetBlock } }) => ({ 22 transforms: { 23 resetBlock(options) { 24 if ( 25 editor.api.block({ 26 at: options?.at, 27 match: { type: editor.getType(CodeBlockPlugin.key) }, 28 }) 29 ) { 30 unwrapCodeBlock(editor); 31 return; 32 } 33 34 return resetBlock(options); 35 }, 36 }, 37}));

@udecode/plate-resizable

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-select

Breaking Changes

  • The following plugins now default to editOnly: true. This means their core functionalities (handlers, rendering injections, etc.) will be disabled when the editor is in read-only mode. To override this behavior for a specific plugin, configure its editOnly field. For example, SomePlugin.configure({ editOnly: false }). (#4327)

  • Package @udecode/plate-select has been deprecated. (#4327)

  • SelectOnBackspacePlugin has been removed. This behavior is now built into Plate by default: delete (backward/forward) at the start of a block will select the previous/next void block instead of removing it.

  • DeletePlugin has been removed. This behavior is now built into Plate by default: delete (backward/forward) from an empty block will remove it instead of merging.

  • RemoveEmptyNodesPlugin has been removed. This behavior is now available through the rules: { normalize: { removeEmpty: true } } configuration on individual plugins.

  • Migration:

    • Remove @udecode/plate-select from your dependencies.
    • Remove any usage of SelectOnBackspacePlugin, DeletePlugin from your project.
    • Replace RemoveEmptyNodesPlugin.configure({ options: { types: ['custom'] } }) with CustomPlugin.configure({ rules: { normalize: { removeEmpty: true } } }). This is used by our LinkPlugin.

@udecode/plate-selection

Breaking Changes

  • The following plugins now default to editOnly: true. This means their core functionalities (handlers, rendering injections, etc.) will be disabled when the editor is in read-only mode. To override this behavior for a specific plugin, configure its editOnly field. For example, SomePlugin.configure({ editOnly: false }). (#4327)

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-slash-command

Breaking Changes

  • The following plugins now default to editOnly: true. This means their core functionalities (handlers, rendering injections, etc.) will be disabled when the editor is in read-only mode. To override this behavior for a specific plugin, configure its editOnly field. For example, SomePlugin.configure({ editOnly: false }). (#4327)

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

  • The type TSlashInputElement has been removed. (#4327)

  • Use TComboboxInputElement from platejs instead for Slash Command input elements, as slash command functionality is built upon the combobox.

@udecode/slate

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

  • Replaced editor.api.shouldMergeNodesRemovePrevNode with editor.api.shouldMergeNodes. shouldMergeNodes is now controlling the remove + merge behavior (#4327)

    text
    1 - Returns `true` if the default merging behavior should be applied. 2 - Returns `false` if the default merging behavior should not be applied. This is used by Plate to prevent void blocks deletion, and to prioritize empty block deletion over merging. 3 4 ```ts 5 // Before 6 editor.api.shouldMergeNodesRemovePrevNode(prev, current); 7 8 // After 9 editor.api.shouldMergeNodes(prev, current); 10 ```
    • Replace editor.api.fragment option structuralTypes with unwrap.

      ts
      1// Before 2editor.api.fragment(editor.selection, { structuralTypes: ['table'] }); 3 4// After 5editor.api.fragment(editor.selection, { unwrap: ['table'] });

Features

  • editor.tf.insertSoftBreak now inserts a soft break instead of a hard break. (#4327)

@udecode/plate-suggestion

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-tabbable

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-table

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-tag

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-test-utils

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-toc

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-toggle

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

@udecode/plate-trailing-block

Breaking Changes

  • The following plugins now default to editOnly: true. This means their core functionalities (handlers, rendering injections, etc.) will be disabled when the editor is in read-only mode. To override this behavior for a specific plugin, configure its editOnly field. For example, SomePlugin.configure({ editOnly: false }). (#4327)

  • Package @udecode/plate-trailing-block has been deprecated. (#4327)

  • Its functionality (e.g., TrailingBlockPlugin) has been moved to @platejs/utils (which is re-exported via platejs).

  • Migration:

    • Remove @udecode/plate-trailing-block from your dependencies.
    • Update import paths to use platejs (e.g., import { TrailingBlockPlugin } from 'platejs';).

@udecode/plate-utils

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

  • Node type definitions (e.g., TImageElement, TParagraphElement) previously co-located with their respective plugin packages (like @udecode/plate-media) have been centralized into @platejs/utils. These are typically re-exported via the main platejs package. (#4327)

    text
    1 - Migration: Update imports for these types to pull from `platejs`. 2 3 ```tsx 4 // Before 5 // import { TImageElement } from '@udecode/plate-media'; 6 7 // After 8 import { TImageElement } from 'platejs'; 9 ```
    • Removed structuralTypes option from useSelectionFragment and useSelectionFragmentProp. These hooks now automatically use plugin.node.isContainer from enabled plugins.

    • Removed:

      • createNodesHOC
      • createNodesWithHOC
      • createNodeHOC
    • Removed usePlaceholderState hook.

      • Migration: Use the BlockPlaceholderPlugin (typically from platejs) instead of the withPlaceholders HOC and usePlaceholderState. Configure placeholders directly within the BlockPlaceholderPlugin options.
        ts
        1// Example BlockPlaceholderPlugin configuration 2BlockPlaceholderPlugin.configure({ 3 options: { 4 className: 5 'before:absolute before:cursor-text before:opacity-30 before:content-[attr(placeholder)]', 6 placeholders: { 7 [ParagraphPlugin.key]: 'Type something...', 8 // ...other placeholders 9 }, 10 query: ({ editor, path }) => { 11 // Example query: only show for top-level empty blocks 12 return ( 13 path.length === 1 && editor.api.isEmpty(editor.children[path[0]]) 14 ); 15 }, 16 }, 17});

Features

  • New plugin SingleBlockPlugin to restrict editor content to a single block while preserving line breaks, while SingleLinePlugin prevents all line breaks. (#4327)

    • @platejs/utils (and by extension, platejs) now exports a comprehensive KEYS object containing all official plugin keys.

      • This is intended to improve decoupling and provide a centralized way to reference plugin keys.

      • Example Usage:

        ts
        1import { KEYS } from 'platejs'; 2 3// Instead of: ParagraphPlugin.key 4// Use: KEYS.p
    • Many node type definitions (e.g., TParagraphElement, TLinkElement) are also now exported from platejs, in addition to being available from their specific plugin packages if those still exist or from @platejs/basic-nodes.

@udecode/plate-yjs

Breaking Changes

  • Renamed all @udecode/plate-* packages to @platejs/*. Replace @udecode/plate- with @platejs/ in your code. (#4327)

CHANGELOG · v48.0.6...v49.0.0 · By @zbeyens