Skip to content

[Bug]: Table: clicks on unmapped DOM regions desync selection and crash toSlateRange on typing/paste #4956

Open

Description

@caspersf

Bug type

Behavior bug (incorrect output/state without crash)

Browser surface

Yes

Summary

When 2 tables exist one below the other in an editor with no empty block between them, clicking away within the editor(somewhere beside the first table) causes the table call to lose focus, and subsequent typing causes content to render below the 1st table, with a console error

Public reproduction URL

https://platejs.org/docs/table

Steps to reproduce

Refer #4952

On https://platejs.org/docs/table (or any editor built from the docs demo with or without TableKit):

  1. Insert two tables (or one table followed by no trailing paragraph).
  2. Put the caret in a cell and type a few characters.
  3. Click in the gap between the two tables (or just below the last table). Note the caret disappears visually — editor.selection did not update because the click target has no data-slate-node ancestor.
  4. Type text. The text is inserted into the last cell (where the caret was) rather than where you clicked, because editor.selection is stale.
  5. Click outside again, type again. Repeat.
  6. After a few iterations, native contentEditable insertion ends up in the unmapped DOM region (visible text appears below the table with no Slate block backing it), and the next interaction throws:
  7. Error: Cannot resolve a Slate point from DOM point: [object HTMLDivElement],

Variant (paste-only, no repeated typing needed):

3a. After step 3 above, press Ctrl+V with anything rich in the clipboard (e.g. a previously copied paragraph or cell from within the editor).

Observed: immediate throw of the same error.

Expected behavior

licks inside the editor should always produce a valid editor.selection, either by ensuring a clickable Slate-mapped region exists for every reachable pixel (e.g., a trailing paragraph after the last table + inter-table paragraph normalization in the table plugin), or by toSlateRange never throwing on user-interaction paths (returning null and falling through).

Nice-to-have: when a click lands outside any table (in an unmapped region of the editor), the selection could be snapped to the last cell of the nearest table — the same behavior that already happens when only a single table is present. With a single table, clicking below/outside the table keeps focus in the table's last cell; with multiple tables, that fallback is lost and focus/selection disappears entirely.

Actual behavior

The current behavior is that a single stray click inside the editor can put the editor in an unrecoverable state, and render subsequent user input in unexpected locations.

Acceptance criteria

Clicks inside the editor should always produce a valid editor.selection, either by ensuring a clickable Slate-mapped region exists for every reachable pixel (e.g., a trailing paragraph after the last table + inter-table paragraph normalization in the table plugin), or by toSlateRange never throwing on user-interaction paths (returning null and falling through).

Plate / Slate / React versions

"platejs": "^52.0.17",
"slate": "^0.103.0",
"slate-hyperscript": "^0.100.0",
"react": "^18.3.0",

Operating system

Latest mac, ubuntu and windows

Install method

No response

Framework / app setup

Plain react

Affected packages or entry points

"@platejs/table": "^52.0.11",

Minimal code or config excerpt

NOT_ENOUGH_INFO

Logs, screenshots, and evidence

Screenshots

Refer https://github.com/udecode/plate/discussions/4952#:~:text=0.120.0-,Screenshots,-The%20ccsc%20text

Logs
Uncaught Error: Cannot resolve a Slate point from DOM point: [object HTMLDivElement],2
at Object.toSlatePoint (index.es.js)
at Object.toSlateRange (index.es.js)
at HTMLDivElement. (index.es.js)

Impact and severity

No response

Additional information

No response

Activity

  1. dosubot commented on Apr 24, 2026

    @dosubot

    This was already discussed in detail in Discussion #4952 — the root cause is that slate-react's onDOMBeforeInput handler has two toSlateRange calls using suppressThrow: false. When a click lands on an unmapped DOM region (no data-slate-node ancestor), the DOM selection drifts but editor.selection stays stale. The next typing or paste event hits those calls and throws.

    Plate's own toSlateRange wrapper does suppress errors [1], but these event handlers in slate-react call ReactEditor.toSlateRange directly, bypassing it.

    The table plugin's normalization [2] currently does not ensure paragraphs between adjacent tables or after the last table — that's delegated to TrailingBlockPlugin [3], which only covers the document's last node.

    Two fixes are needed:

    1. In slate-react: The two unsafe toSlateRange calls in onDOMBeforeInput should use suppressThrow: true or be wrapped in try/catch, consistent with the other calls in the same file that already do this [4].

    2. In the table plugin: withNormalizeTable could ensure a mapped block exists between adjacent tables, so there's always a valid click target in those gaps.

    As a partial workaround, adding TrailingBlockPlugin to your setup ensures at least a trailing paragraph exists after the last table, reducing the frequency of the crash — but it won't prevent it for inter-table gaps or editor padding regions, as @caspersf noted.

    To reply, just mention @dosu.


    Share context across your team and agents. Try Dosu.

    Leave Feedback Learn about plate with Dosu Add Dosu to your team

  2. zbeyens commented on Apr 25, 2026

    @zbeyens
    Member

    ✅ Fixed in #4967.

    QA:

    • Open /docs/table.
    • Click inside a table cell, then click an unmapped editor-root region beside the table.
    • Type or paste rich content.
    • Confirm text stays inside the Slate value and no toSlateRange / Cannot resolve a Slate point from DOM point error appears.
  3. caspersf commented on Apr 27, 2026

    @caspersf
    Author

    @zbeyens, just wanted to confirm if this has been deployed. I still see the issue in https://platejs.org/docs/table

  4. zbeyens commented on Apr 27, 2026

    @zbeyens
    Member

    No, I'll close that PR. The fix is too dirty to be released. You could try it on your end.

  5. filippodev-tech commented on Jul 2, 2026

    @filippodev-tech

    @zbeyens hi, I am having the same issue, after implementing the HTML setup.
    My deps:

    {
      "dependencies": {
        "@platejs/basic-nodes": "^53.0.0",
        "@platejs/basic-styles": "^53.0.0",
        "@platejs/callout": "^53.0.0",
        "@platejs/caption": "^53.0.0",
        "@platejs/code-block": "^53.0.0",
        "@platejs/code-drawing": "^53.0.0",
        "@platejs/comment": "^53.0.0",
        "@platejs/date": "^53.0.0",
        "@platejs/footnote": "^53.0.0",
        "@platejs/layout": "^53.0.0",
        "@platejs/link": "^53.0.3",
        "@platejs/list": "^53.1.3",
        "@platejs/markdown": "^53.2.2",
        "@platejs/media": "^53.1.4",
        "@platejs/mention": "^53.0.0",
        "@platejs/suggestion": "^53.2.3",
        "@platejs/table": "^53.0.9",
        "@platejs/toc": "^53.0.0",
        "@platejs/toggle": "^53.0.0",
        "next": "^15.5.14",
        "platejs": "^53.2.1",
      },
      "overrides": {
        "slate": "0.118.0",
        "slate-dom": "0.117.4",
        "slate-react": "0.117.1"
      }
    }

    and my plugins:

    import { BaseAlignKit } from "./plugins/align-base-kit";
    import { BaseBasicBlocksKit } from "./plugins/basic-blocks-base-kit";
    import { BaseCalloutKit } from "./plugins/callout-base-kit";
    import { BaseCodeBlockKit } from "./plugins/code-block-base-kit";
    import { BaseColumnKit } from "./plugins/column-base-kit";
    import { BaseDateKit } from "./plugins/date-base-kit";
    import { BaseFootnoteKit } from "./plugins/footnote-base-kit";
    import { BaseFontKit } from "./plugins/font-base-kit";
    import { BaseLineHeightKit } from "./plugins/line-height-base-kit";
    import { BaseListKit } from "./plugins/list-base-kit";
    import { MarkdownKit } from "./plugins/markdown-kit";
    import { BaseMediaKit } from "./plugins/media-base-kit";
    import { BaseMentionKit } from "./plugins/mention-base-kit";
    import { BaseTableKit } from "./plugins/table-base-kit";
    import { BaseTocKit } from "./plugins/toc-base-kit";
    import { BaseToggleKit } from "./plugins/toggle-base-kit";
    import { TrailingBlockPlugin } from "platejs";
    
    export const BasicFormattingKit = [
      ...BaseBasicBlocksKit,
      ...BaseCodeBlockKit,
      ...BaseTableKit,
      ...BaseToggleKit,
      ...BaseTocKit,
      ...BaseMediaKit,
      ...BaseCalloutKit,
      ...BaseColumnKit,
      ...BaseDateKit,
      ...BaseMentionKit,
      ...BaseFontKit,
      ...BaseListKit,
      ...BaseAlignKit,
      ...BaseLineHeightKit,
      ...MarkdownKit,
      ...BaseFootnoteKit,
      TrailingBlockPlugin,
    ];
    import {
      BoldRules,
      HighlightRules,
      ItalicRules,
      MarkComboRules,
      StrikethroughRules,
      SubscriptRules,
      SuperscriptRules,
      UnderlineRules,
    } from "@platejs/basic-nodes";
    import {
      BoldPlugin,
      HighlightPlugin,
      ItalicPlugin,
      KbdPlugin,
      StrikethroughPlugin,
      SubscriptPlugin,
      SuperscriptPlugin,
      UnderlinePlugin,
    } from "@platejs/basic-nodes/react";
    
    import { HighlightLeaf } from "./HighlightNode";
    import { KbdLeaf } from "@/components/CaseClosing/RichTextArea/KbdNode";
    import { LinkPlugin } from "@platejs/link/react";
    import { LinkRules } from "@platejs/link";
    import { LinkElement } from "./LinkNode";
    import { LinkFloatingToolbar } from "@/components/CaseClosing/RichTextArea/LinkToolbar";
    
    export const BasicMarksKit = [
      BoldPlugin.configure({
        inputRules: [
          BoldRules.markdown({ variant: "*" }),
          BoldRules.markdown({ variant: "_" }),
          MarkComboRules.markdown({ variant: "boldItalic" }),
          MarkComboRules.markdown({ variant: "boldUnderline" }),
          MarkComboRules.markdown({ variant: "boldItalicUnderline" }),
          MarkComboRules.markdown({ variant: "italicUnderline" }),
        ],
      }),
      ItalicPlugin.configure({
        inputRules: [
          ItalicRules.markdown({ variant: "*" }),
          ItalicRules.markdown({ variant: "_" }),
        ],
      }),
      UnderlinePlugin.configure({
        inputRules: [UnderlineRules.markdown()],
      }),
      LinkPlugin.configure({
        inputRules: [
          LinkRules.markdown(),
          LinkRules.autolink({ variant: "paste" }),
          LinkRules.autolink({ variant: "space" }),
          LinkRules.autolink({ variant: "break" }),
        ],
        render: {
          node: LinkElement,
          afterEditable: () => <LinkFloatingToolbar />,
        },
      }),
      // currently not in use
      StrikethroughPlugin.configure({
        inputRules: [StrikethroughRules.markdown()],
        shortcuts: { toggle: { keys: "mod+shift+x" } },
      }),
      SubscriptPlugin.configure({
        inputRules: [SubscriptRules.markdown()],
        shortcuts: { toggle: { keys: "mod+comma" } },
      }),
      SuperscriptPlugin.configure({
        inputRules: [SuperscriptRules.markdown()],
        shortcuts: { toggle: { keys: "mod+period" } },
      }),
      HighlightPlugin.configure({
        inputRules: [
          HighlightRules.markdown({ variant: "==" }),
          HighlightRules.markdown({ variant: "≡" }),
        ],
        node: { component: HighlightLeaf },
        shortcuts: { toggle: { keys: "mod+shift+h" } },
      }),
      KbdPlugin.withComponent(KbdLeaf),
    ];
    
      const editor = usePlateEditor({
        plugins: [...BasicFormattingKit, ...BasicMarksKit],
        value: [...],
      });

    your help will be much appreciated

  6. added theissue type on Oct 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions