plate icon indicating copy to clipboard operation
plate copied to clipboard

Markdown Empty List Deserialization Error

Open davidgolden opened this issue 6 months ago • 1 comments

Description

Deserializing an initial markdown value of

foo

*

bar

generates the Slate value of

[
{type: "p", children: [{text: "foo"}]},
{type: "ul", children: [{type: "li", children: []}]},
{type: "p", children: [{text: "bar"}]}
]

When editor receives focus, TypeError: point is undefined is thrown.

Somewhere in the deserialization process, Plate is not handling a list item with no content.

I believe the correct Slate value should be

[
{type: "p", children: [{text: "foo"}]},
{type: "ul", children: [{type: "li", children: [{type: "lic", children: []}]}]},
{type: "p", children: [{text: "bar"}]}
]

Reproduction URL

No response

Reproduction steps

1. Create a Plate editor with MarkdownPlugin and initial value from above.
2. Place focus in editor

Plate version

48.0.2 (@udecode/plate-markdown)

Slate React version

0.114.2

Screenshots


Logs


Browsers

No response

davidgolden avatar Jul 14 '25 18:07 davidgolden

Hi @davidgolden! I'm Dosu and I’m helping the plate team.

This happens because Plate's markdown deserializer can output empty list item nodes (li with no children) when the markdown contains an empty list item like *. Slate expects every list item to have at least one child (usually a text node), so when it encounters an empty li, it throws TypeError: point is undefined.

This isn't normalized by default in Plate v48.x, and there are no built-in checks for this case. In newer Plate versions (v49+), you can customize deserialization rules to fix this. The workaround is to override the deserializer for list items so it always adds a default child if none exist.

Here's a minimal example for Plate v49+ (for v48.x, the pattern is similar but uses nodes instead of rules):

import { MarkdownPlugin } from '@udecode/plate-markdown';

export const markdownPlugin = MarkdownPlugin.configure({
  options: {
    rules: {
      li: {
        deserialize: (mdastNode, deco, options) => {
          // your normal deserialization logic...
          const children = /* ... */;
          // Ensure at least one child
          return {
            type: 'li',
            children: children.length ? children : [{ text: '' }],
          };
        },
      },
    },
  },
});

This ensures every list item has at least one child, preventing the runtime error. If you're on v48.x, use the nodes option instead of rules in your plugin config. There’s no out-of-the-box example for this, but the customization pattern is supported and documented in the Plate markdown plugin API (see PRs and docs).

Let me know if you need a more concrete example for your exact Plate version.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Join Discord Share on X

dosubot[bot] avatar Jul 14 '25 18:07 dosubot[bot]