Skip to content

fix: stop crashing on a span where a block belongs and on children: null - #3695

Draft
christianhg wants to merge 3 commits into
mainfrom
fix/stray-span-as-object
Draft

christianhg wants to merge 3 commits into
mainfrom
fix/stray-span-as-object

Conversation

@christianhg

@christianhg christianhg commented Nov 13, 2026 •

Copy link
Copy Markdown
Member

A span that arrives where a block belongs no longer crashes the editor. This happens inside a callout, a nested callout or a table cell, through the start value, update value or a collaborator's patches. It also happens at the root through a collaborator's patches. The span is now drawn as a block object, like a block whose type the schema does not know. Its fields stay as received and its text is not shown. The editor emits no correction patches for it, except to rename a _key it shares with a sibling. A missing or empty _key stays as received. Clicking it selects it, and Backspace deletes it with one unset patch. At the root, the start value and update value still report invalid value with the "Remove the block" resolution, as before.

A text block with children: null no longer crashes validation. It gets the same invalid value event as any other children that is not an array, which offers to reset children to an empty array. The proposed patches are not applied.

@vercel

vercel Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
portable-text-editor-documentation Ready Ready Preview Nov 13, 2026 3:20pm UTC
portable-text-example-basic Ready Ready Preview Nov 13, 2026 3:20pm UTC
portable-text-playground Ready Ready Preview Nov 13, 2026 3:20pm UTC

Request Review

@changeset-bot

changeset-bot Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ebea023

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 14 packages
Name Type
@portabletext/editor Patch
@portabletext/plugin-character-pair-decorator Patch
@portabletext/plugin-decorations Patch
@portabletext/plugin-dnd Patch
@portabletext/plugin-emoji-picker Patch
@portabletext/plugin-input-rule Patch
@portabletext/plugin-list-index Patch
@portabletext/plugin-markdown-shortcuts Patch
@portabletext/plugin-one-line Patch
@portabletext/plugin-paste-link Patch
@portabletext/plugin-table Patch
@portabletext/plugin-typeahead-picker Patch
@portabletext/plugin-typography Patch
@portabletext/toolbar Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

github-actions Bot commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

Bundle Stats

✅ No significant changes.

All scenario measurements (34)

🗺️ @portabletext/block-tools / @portabletext/block-tools · @portabletext/block-tools / @portabletext/block-tools/rules · @portabletext/editor / @portabletext/editor · @portabletext/editor / @portabletext/editor/behaviors · @portabletext/editor / @portabletext/editor/plugins · @portabletext/editor / @portabletext/editor/selectors · @portabletext/editor / @portabletext/editor/traversal · @portabletext/editor / @portabletext/editor/utils · @portabletext/editor / defineSchema · @portabletext/editor / useEditor · @portabletext/editor / typical · @portabletext/html / @portabletext/html · @portabletext/html / @portabletext/html/rules · @portabletext/html / htmlToPortableText · @portabletext/keyboard-shortcuts / @portabletext/keyboard-shortcuts · @portabletext/patches / @portabletext/patches · @portabletext/repair / @portabletext/repair · @portabletext/sanity-bridge / @portabletext/sanity-bridge · @portabletext/schema / @portabletext/schema · @portabletext/plugin-character-pair-decorator / @portabletext/plugin-character-pair-decorator · @portabletext/plugin-decorations / @portabletext/plugin-decorations · @portabletext/plugin-dnd / @portabletext/plugin-dnd · @portabletext/plugin-emoji-picker / @portabletext/plugin-emoji-picker · @portabletext/plugin-input-rule / @portabletext/plugin-input-rule · @portabletext/plugin-list-index / @portabletext/plugin-list-index · @portabletext/plugin-markdown-shortcuts / @portabletext/plugin-markdown-shortcuts · @portabletext/plugin-one-line / @portabletext/plugin-one-line · @portabletext/plugin-paste-link / @portabletext/plugin-paste-link · @portabletext/plugin-typeahead-picker / @portabletext/plugin-typeahead-picker · @portabletext/plugin-typography / @portabletext/plugin-typography · @portabletext/toolbar / @portabletext/toolbar · @portabletext/toolbar / typical · @portabletext/plugin-table / @portabletext/plugin-table · @portabletext/plugin-table / @portabletext/plugin-table/ui · Artifacts

Scenario Kind Bundle (raw / gzip) Gzip change Import time Import change
⚪ @portabletext/block-tools / @portabletext/block-tools export 91.8 KB / 12.9 KB None 10 ms -1 ms, -10.1%
⚪ @portabletext/block-tools / @portabletext/block-tools/rules export 15.2 KB / 2.1 KB None 3 ms -0 ms, -3.9%
⚪ @portabletext/editor / @portabletext/editor export 1.03 MB / 131.3 KB +128 B, +0.1% 49 ms +1 ms, +1.7%
⚪ @portabletext/editor / @portabletext/editor/behaviors export 4.3 KB / 147 B None 2 ms +0 ms, +13.6%
⚪ @portabletext/editor / @portabletext/editor/plugins export 5.1 KB / 645 B None 6 ms +1 ms, +14.7%
⚪ @portabletext/editor / @portabletext/editor/selectors export 96.3 KB / 9.4 KB +9 B, +0.1% 6 ms +1 ms, +10.9%
⚪ @portabletext/editor / @portabletext/editor/traversal export 46.7 KB / 4.3 KB +21 B, +0.5% 4 ms +0 ms, +5.5%
⚪ @portabletext/editor / @portabletext/editor/utils export 32.8 KB / 4.1 KB None 4 ms -0 ms, -2.2%
⚪ @portabletext/editor / defineSchema consumer 658 B / 69 B None None None
⚪ @portabletext/editor / typical consumer 1.02 MB / 131.1 KB +130 B, +0.1% None None
⚪ @portabletext/editor / useEditor consumer 1.6 KB / 248 B None None None
⚪ @portabletext/html / @portabletext/html export 69.3 KB / 10.1 KB None 5 ms -0 ms, -0.1%
⚪ @portabletext/html / @portabletext/html/rules export 15.2 KB / 2.1 KB None 3 ms +0 ms, +0.1%
⚪ @portabletext/html / htmlToPortableText consumer 69.3 KB / 10.1 KB None None None
⚪ @portabletext/keyboard-shortcuts / @portabletext/keyboard-shortcuts export 7.0 KB / 858 B None 2 ms +0 ms, +2.8%
⚪ @portabletext/patches / @portabletext/patches export 50.0 KB / 7.7 KB None 3 ms -0 ms, -3.7%
⚪ @portabletext/plugin-character-pair-decorator / @portabletext/plugin-character-pair-decorator export 124.1 KB / 15.8 KB None None None
⚪ @portabletext/plugin-decorations / @portabletext/plugin-decorations export 16.4 KB / 2.3 KB None None None
⚪ @portabletext/plugin-dnd / @portabletext/plugin-dnd export 10.4 KB / 1.8 KB None None None
⚪ @portabletext/plugin-emoji-picker / @portabletext/plugin-emoji-picker export 150.1 KB / 18.6 KB None None None
⚪ @portabletext/plugin-input-rule / @portabletext/plugin-input-rule export 124.2 KB / 15.8 KB None None None
⚪ @portabletext/plugin-list-index / @portabletext/plugin-list-index export 7.4 KB / 1.1 KB None None None
⚪ @portabletext/plugin-markdown-shortcuts / @portabletext/plugin-markdown-shortcuts export 136.4 KB / 17.6 KB None None None
⚪ @portabletext/plugin-one-line / @portabletext/plugin-one-line export 2.6 KB / 608 B None None None
⚪ @portabletext/plugin-paste-link / @portabletext/plugin-paste-link export 4.3 KB / 1.0 KB None None None
⚪ @portabletext/plugin-table / @portabletext/plugin-table export 61.1 KB / 7.1 KB None None None
⚪ @portabletext/plugin-table / @portabletext/plugin-table/ui export 114.6 KB / 19.5 KB None None None
⚪ @portabletext/plugin-typeahead-picker / @portabletext/plugin-typeahead-picker export 166.2 KB / 19.9 KB None None None
⚪ @portabletext/plugin-typography / @portabletext/plugin-typography export 131.1 KB / 17.0 KB None None None
⚪ @portabletext/repair / @portabletext/repair export 4.8 KB / 704 B None 2 ms +0 ms, +3.3%
⚪ @portabletext/sanity-bridge / @portabletext/sanity-bridge export 27.0 KB / 3.0 KB None 2 ms -0 ms, -5.4%
⚪ @portabletext/schema / @portabletext/schema export 9.9 KB / 1.2 KB None 1 ms -0 ms, -7.1%
⚪ @portabletext/toolbar / @portabletext/toolbar export 153.3 KB / 17.3 KB None None None
⚪ @portabletext/toolbar / typical consumer 145.6 KB / 16.9 KB None None None

Significant means at least 1.0 KB and 1% gzip, or at least 5 ms and 10% import time.

Pin rendering and typing around spans where blocks belong.
Assert preservation of keyless spans without `text` or `marks`.
Assert duplicate-key repair after collaborator inserts at each depth.
Apply the Backspace patch with `applyAll` to verify it removes only
that span. Pin exact undo and redo patches for keyed and keyless spans.

Move the six `children: null` validation cases to `validateValue` unit
tests. Keep the `children: "oops"` control there too. Retain a browser
case that types after the invalid-value event without applying its
resolution.
`useChildren` sent every span to the text renderer. At a block position
the span has no text block parent, so the renderer threw.

Render those spans as selectable block objects. Keep their text hidden.
Narrow the node once with `isTypedObject` before comparing its type.
`isSpanNode` does not provide the index signature the object renderer
requires.

`normalizeNode` retains only duplicate-key corrections for those spans.
Main's repair renames the later copy of a shared key. Reuse the normal
correction path so its dirty-path re-queue still runs. Skip every other
correction. Missing and empty `_key` values stay as received.

`isLeafObject` recognizes the span as a leaf object. Check the empty
container registry before the span guard in `isEditableContainer`.
`isLeafObject` and `isEditableContainer` call `isBlock` only for a span.

`getDomNodePath` turns an all-digit last segment into an index only on a
block object element. Field names made of digits stay strings.
Backspace deletes the selected span with one `unset` patch.
`validateValue` let `children: null` reach array methods and throw.
Treat `null` as an invalid `children` property. The editor emits an
`invalid value` event with a resolution that resets `children` to an
empty array. It does not apply the proposed patches.

`children` set to `false`, `0` or `''` still throws.

This branch was successfully deployed

3 active deployments
Preview – portable-text-editor-documentation — ebea023e Deployed Nov 13, 2026 by vercel[bot]
Preview – portable-text-playground — ebea023e Deployed Nov 13, 2026 by vercel[bot]
Preview – portable-text-example-basic — ebea023e Deployed Nov 13, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant