docs: add a Containers concept page and a render-props migration guide - #3022
docs: add a Containers concept page and a render-props migration guide#3022christianhg wants to merge 3 commits into
Conversation
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
📦 Bundle Stats —
|
| Metric | Value | vs main (c713c21) |
|---|---|---|
| Internal (raw) | 807.5 KB | - |
| Internal (gzip) | 155.1 KB | - |
| Bundled (raw) | 1.42 MB | - |
| Bundled (gzip) | 319.4 KB | - |
| Import time | 98ms | -3ms, -2.7% |
@portabletext/editor/behaviors
| Metric | Value | vs main (c713c21) |
|---|---|---|
| Internal (raw) | 467 B | - |
| Internal (gzip) | 207 B | - |
| Bundled (raw) | 424 B | - |
| Bundled (gzip) | 171 B | - |
| Import time | 2ms | -0ms, -0.9% |
@portabletext/editor/plugins
| Metric | Value | vs main (c713c21) |
|---|---|---|
| Internal (raw) | 2.7 KB | - |
| Internal (gzip) | 894 B | - |
| Bundled (raw) | 2.5 KB | - |
| Bundled (gzip) | 827 B | - |
| Import time | 7ms | -0ms, -2.5% |
@portabletext/editor/selectors
| Metric | Value | vs main (c713c21) |
|---|---|---|
| Internal (raw) | 82.7 KB | - |
| Internal (gzip) | 15.4 KB | - |
| Bundled (raw) | 78.4 KB | - |
| Bundled (gzip) | 14.3 KB | - |
| Import time | 8ms | +0ms, +0.2% |
@portabletext/editor/traversal
| Metric | Value | vs main (c713c21) |
|---|---|---|
| Internal (raw) | 28.1 KB | - |
| Internal (gzip) | 5.6 KB | - |
| Bundled (raw) | 28.1 KB | - |
| Bundled (gzip) | 5.5 KB | - |
| Import time | 6ms | -0ms, -3.2% |
@portabletext/editor/utils
| Metric | Value | vs main (c713c21) |
|---|---|---|
| Internal (raw) | 30.6 KB | - |
| Internal (gzip) | 6.4 KB | - |
| Bundled (raw) | 28.2 KB | - |
| Bundled (gzip) | 6.1 KB | - |
| Import time | 6ms | -0ms, -0.9% |
🗺️ . · ./behaviors · ./plugins · ./selectors · ./traversal · ./utils · Artifacts
Details
- Import time regressions over 10% are flagged with
⚠️ - Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.
📦 Bundle Stats — @portabletext/markdown
Compared against main (c713c214)
| Metric | Value | vs main (c713c21) |
|---|---|---|
| Internal (raw) | 53.8 KB | - |
| Internal (gzip) | 9.8 KB | - |
| Bundled (raw) | 348.9 KB | - |
| Bundled (gzip) | 96.3 KB | - |
| Import time | 38ms | -2ms, -4.5% |
🗺️ View treemap · Artifacts
Details
- Import time regressions over 10% are flagged with
⚠️ - Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.
cab06af to
90b1bcb
Compare
90b1bcb to
77a88e3
Compare
60779b9 to
cb9381b
Compare
cb9381b to
06a3cdb
Compare
1cef1d5 to
9d8cc0a
Compare
9d8cc0a to
5baa3db
Compare
5984bb0 to
e9eae7b
Compare
e9eae7b to
7e42ead
Compare
7e42ead to
d6083a8
Compare
d6083a8 to
f5d9ec8
Compare
f5d9ec8 to
961db7a
Compare
961db7a to
aacca05
Compare
aacca05 to
f262c68
Compare
Containers (block objects with editable child arrays: callouts, code
blocks, tables) had no page on the docs site despite being the third
core editor concept next to the schema and Behaviors, and despite
shipping consumers (`plugin-table`, the playground's callout/code-block/
fact-box/table examples).
Add `editor/concepts/containers` covering: the value model (a container
is plain Portable Text, no editor-specific format), declaring the schema
with a nested `{type: 'block'}` member and the three sub-schema
resolution rules (declared / declared-empty / absent), registering with
`defineContainer` + `NodePlugin` (render contract: spread `attributes`,
render `children`, `contentEditable={false}` chrome, `renderDefault`
fallback, stable `nodes` identity), and nesting through `of` with the
table example from `defineContainer`'s own jsdoc.
A render-pipeline section covers what registering opts you into: your
render owns the outer wrapper (legacy block-level render props don't
compose; span-level props and range decorations keep working), with a
combined example registering the kinds through one `NodePlugin`
(`defineContainer`, `defineTextBlock` with `type: 'block'`, which is
the text block type at every nesting level, and `defineBlockObject`,
which must render its `children`). The consequences get their remedies:
list wrapping and numbering via `plugin-list-index`, drop indicators
via `plugin-dnd`, with a worked text-block render composing both hooks.
A migration subsection routes the legacy props into registrations:
catch-all `defineBlockObject`/`defineInlineObject` registrations
(`type: '*'`) as the direct equivalents of the generic
`renderBlock`/`renderChild` callbacks, and a `type: 'block'` text-block
registration reproducing the legacy nesting order (style innermost,
list item, block outermost) as read from `render.text-block.tsx`.
The editing-semantics section carries a worked example (a code-block
sub-schema forbidding decorators plus a schema-lookup-configured
Markdown shortcut, showing the schema acting as the feature flag),
followed by registration validation (warn-and-skip naming the mismatch)
and pointers to `plugin-table` and the playground.
Registered in the Concepts sidebar group after Behaviors. Built with
`CHECK_LINKS=1`, all links validate.
The containers page's migration subsection was outgrowing its host: the legacy-to-registrations story spans all four block-level render props, two catch-all patterns, incremental strategy, and the chrome the engine stops drawing, too much for a concept page aside, not enough anywhere else. Add `editor/guides/migrate-render-props`, a kind-by-kind guide: a what-maps-to-what table; the per-`_type` dispatch fact that makes incremental migration safe (registered types render through the new pipeline, everything else keeps the legacy path); block objects first (the per-type registration replacing the `schemaType.name` branch, the `'*'` catch-all as the direct equivalent of the generic legacy callback, and both composing); inline objects the same way; spans for the rare `renderChild` that customized them (`defineSpan`, whose `children` arrive with `renderDecorator`/`renderAnnotation` already applied, as read from `render.span.tsx`); text blocks as `type: 'block'` folding `renderStyle`/`renderListItem`/`renderBlock` into one render in the legacy nesting order; the chrome reclaim step (`plugin-list-index`, `plugin-dnd`); and the final mount-and-clean-up showing which props stay (span-level: `renderDecorator`, `renderAnnotation`, `renderPlaceholder`, range decorations). The before-sides are the existing docs' own examples (`custom-blocks`'s image `renderBlock` and stock-ticker `renderChild`, getting-started's `renderStyle`), so before and after describe the same editor. The containers concept page's migration subsection shrinks to a pointer at the guide. Registered in the Guides sidebar group after custom rendering. Built with `CHECK_LINKS=1`, all links validate.
…ides The custom rendering guide claimed "Portable Text has no concept of block nesting", written before containers existed and now contradicted by the containers concept page one sidebar group away. The true claim was always about lists: a list is a flat run of sibling text blocks carrying `listItem` and `level`, with no wrapper node, and that stays true with containers, which nest blocks through object fields. Restate it as the list-specific fact with a pointer to the containers page. The guides' prerequisite notes had also drifted from the released majors: seven pages claimed `@portabletext/editor` v6.x (current: 7.x) and two claimed `@portabletext/toolbar` v7.x (current: 8.x). Refresh all of them; the `@portabletext/react` v6.x note on the rendering page is still accurate and stays.
|
|
||
| Omitting `render` falls through to the engine default, so a registration can exist purely to mark the field as editable. | ||
|
|
||
| Registering a container also normalizes the value: existing blocks of the registered type immediately pick up the container shape, so a `table` block without a `rows` array gains `rows: []`. |
There was a problem hiding this comment.
Claude suggests that this isn't completely accurate?
rows: [] is wrong (containers.mdx, "Register the container"). Normalization doesn't leave an empty array — it seeds a cursor-ready chain. normalize-node.ts synthesizes a child, and packages/editor/tests/container-normalization.test.tsx asserts {_type: 'table', _key} becomes rows → row → cells → cell → content → block → span('') in three set patches. {[field]: []} is only reached when arrayField.of is empty. Suggest: "gains a cursor-ready rows array seeded with an empty row, cell, and text block."
markmichon
left a comment
There was a problem hiding this comment.
lgtm. left some version bump suggestions surfaced from the peerdeps list.
|
|
||
| :::note[Prerequisites] | ||
| This guide covers `@portabletext/editor` **v6.x** ([changelog](https://github.com/portabletext/editor/releases)). Requires React 18+. | ||
| This guide covers `@portabletext/editor` **v7.x** ([changelog](https://github.com/portabletext/editor/releases)). Requires React 18+. |
There was a problem hiding this comment.
The peerdeps in 7 use react 19
| This guide covers `@portabletext/editor` **v7.x** ([changelog](https://github.com/portabletext/editor/releases)). Requires React 18+. | |
| This guide covers `@portabletext/editor` **v7.x** ([changelog](https://github.com/portabletext/editor/releases)). Requires React 19.2.7+. |
|
|
||
| :::note[Prerequisites] | ||
| This guide covers `@portabletext/toolbar` **v7.x** and `@portabletext/keyboard-shortcuts` **v2.x** ([changelog](https://github.com/portabletext/editor/releases)). Requires `@portabletext/editor` v6.x and React 18+. | ||
| This guide covers `@portabletext/toolbar` **v8.x** and `@portabletext/keyboard-shortcuts` **v2.x** ([changelog](https://github.com/portabletext/editor/releases)). Requires `@portabletext/editor` v7.x and React 18+. |
There was a problem hiding this comment.
| This guide covers `@portabletext/toolbar` **v8.x** and `@portabletext/keyboard-shortcuts` **v2.x** ([changelog](https://github.com/portabletext/editor/releases)). Requires `@portabletext/editor` v7.x and React 18+. | |
| This guide covers `@portabletext/toolbar` **v8.x** and `@portabletext/keyboard-shortcuts` **v2.x** ([changelog](https://github.com/portabletext/editor/releases)). Requires `@portabletext/editor` v7.x and React 19.2.7+. |
|
|
||
| :::note[Prerequisites] | ||
| This guide covers `@portabletext/editor` **v6.x**, `@portabletext/toolbar` **v7.x**, and `@portabletext/keyboard-shortcuts` **v2.x**. Requires React 18+. Check the [editor changelog](https://github.com/portabletext/editor/releases) for breaking changes. | ||
| This guide covers `@portabletext/editor` **v7.x**, `@portabletext/toolbar` **v8.x**, and `@portabletext/keyboard-shortcuts` **v2.x**. Requires React 18+. Check the [editor changelog](https://github.com/portabletext/editor/releases) for breaking changes. |
There was a problem hiding this comment.
| This guide covers `@portabletext/editor` **v7.x**, `@portabletext/toolbar` **v8.x**, and `@portabletext/keyboard-shortcuts` **v2.x**. Requires React 18+. Check the [editor changelog](https://github.com/portabletext/editor/releases) for breaking changes. | |
| This guide covers `@portabletext/editor` **v7.x**, `@portabletext/toolbar` **v8.x**, and `@portabletext/keyboard-shortcuts` **v2.x**. Requires React 19.2.7+. Check the [editor changelog](https://github.com/portabletext/editor/releases) for breaking changes. |
|
|
||
| :::note[Prerequisites] | ||
| This guide covers `@portabletext/editor` **v6.x** and `@portabletext/toolbar` **v7.x** ([changelog](https://github.com/portabletext/editor/releases)). Requires React 18+. You should be familiar with the [getting started guide](/editor/getting-started/) and [custom rendering](/editor/guides/custom-rendering/). | ||
| This guide covers `@portabletext/editor` **v7.x** and `@portabletext/toolbar` **v8.x** ([changelog](https://github.com/portabletext/editor/releases)). Requires React 18+. You should be familiar with the [getting started guide](/editor/getting-started/) and [custom rendering](/editor/guides/custom-rendering/). |
There was a problem hiding this comment.
| This guide covers `@portabletext/editor` **v7.x** and `@portabletext/toolbar` **v8.x** ([changelog](https://github.com/portabletext/editor/releases)). Requires React 18+. You should be familiar with the [getting started guide](/editor/getting-started/) and [custom rendering](/editor/guides/custom-rendering/). | |
| This guide covers `@portabletext/editor` **v7.x** and `@portabletext/toolbar` **v8.x** ([changelog](https://github.com/portabletext/editor/releases)). Requires React 19.2.7+. You should be familiar with the [getting started guide](/editor/getting-started/) and [custom rendering](/editor/guides/custom-rendering/). |
|
|
||
| :::note[Prerequisites] | ||
| This guide covers `@portabletext/editor` **v6.x** ([changelog](https://github.com/portabletext/editor/releases)). Requires React 18+. | ||
| This guide covers `@portabletext/editor` **v7.x** ([changelog](https://github.com/portabletext/editor/releases)). Requires React 18+. |
There was a problem hiding this comment.
| This guide covers `@portabletext/editor` **v7.x** ([changelog](https://github.com/portabletext/editor/releases)). Requires React 18+. | |
| This guide covers `@portabletext/editor` **v7.x** ([changelog](https://github.com/portabletext/editor/releases)). Requires React 19.2.7+. |
Initial pass at docs for the new Container API: https://portable-text-editor-documentation-git-docs-containers-page.sanity.build/editor/concepts/containers/
Containers make the editor understand how to render and traverse into nested editable structures. But there's more to it than that. The Container API provides full control of the rendering, which doesn't really compose with the legacy render callbacks. Therefore, there's also a migration guide that explains the new node registration concept in more detail and in comparison to the legacy render callbacks.
The migration guide is called out inside the Containers page like this: