Skip to content

fix!: render lists using direct nesting mode - #280

Draft
rexxars wants to merge 3 commits into
mainfrom
fix/direct-list-nesting
Draft

fix!: render lists using direct nesting mode#280
rexxars wants to merge 3 commits into
mainfrom
fix/direct-list-nesting

Conversation

@rexxars

@rexxars rexxars commented Aug 11, 2026

Copy link
Copy Markdown
Member

Warning

Blocked. This needs the next major of @portabletext/react, which is not published yet (see portabletext/react-portabletext#340). CI will fail until that lands and the dependency here is bumped. Opened as a draft so the work is not lost.

What this changes

Lists were nested the way HTML needs them to be, where a deeper list has to live inside a list item because HTML cannot put a list directly inside a list. React Native has no such restriction, so this rendered a View inside the Text of the list item above it.

It also meant that levels the author never wrote, such as a list starting at level 3 or one skipping from level 1 to level 3, had to be filled with an empty list item, which draws a bullet or a number with no content next to it.

Passing listNestingMode="direct" nests a deeper list straight inside its parent list instead. Generated levels become bare lists, so they indent without drawing a marker.

Rendering [level 3 number, level 1 number], before:

<View paddingLeft=16>  <Text>"1. "        <- phantom marker
  <View paddingLeft=32>  <Text>"1. "      <- phantom marker
    <View paddingLeft=48>  <Text>"1. " "L3"
  <View>  <Text>"2. " "L1"

After:

<View paddingLeft=16>
  <View paddingLeft=32>
    <View paddingLeft=48>  <Text>"1. " "L3"
  <View>  <Text>"2. " "L1"

Breaking

Nested lists are no longer rendered inside the list item that precedes them, but as a sibling of it, directly inside the parent list. Custom list and listItem components that relied on the old shape may need adjusting. value.level is unchanged and still matches the nesting depth.

engines.node is also raised to >=22.12.

Still to do before this can merge

  • Bump @portabletext/react from ^4.0.3 to the new major once it is published. That is a four major jump, so mergeComponents and the component prop types need verifying, not just a range edit.
  • Optional, but this major would be the moment for it: DefaultList uses paddingLeft: 16 * value.level on a nested View, so indentation compounds to 16 + 32 + 48 = 96 for a level 3 item rather than 48. Now that nesting depth always equals level, a flat paddingLeft: 16 per nested list is the correct fix.

Verification

Verified by linking @portabletext/toolkit@6.0.0 under the existing @portabletext/react@4.0.3, which confirms the rendering change. 4 snapshots updated, 34 pass, type-check clean. Against the currently resolved dependencies, 1 snapshot fails, which is the blocker above.

Refs portabletext/toolkit#100

Lists were nested the way HTML needs them to be, where a deeper list has to
live inside a list item because HTML cannot put a list directly inside a list.
React Native has no such restriction, so this rendered a `View` inside the
`Text` of the list item above it.

It also meant that levels the author never wrote - a list starting at level 3,
or one skipping from level 1 to level 3 - had to be filled with an empty list
item, which draws a bullet or a number with no content next to it. `direct`
mode fills those levels with a bare list instead, so they indent without
drawing a marker.

BREAKING CHANGE: nested lists are no longer rendered inside the list item that
precedes them, but as a sibling of it, directly inside the parent list. Custom
`list` and `listItem` components that relied on the old shape may need
adjusting. `value.level` is unchanged and still matches the nesting depth.
BREAKING CHANGE: Node.js 22.12 or later is required.
@changeset-bot

changeset-bot Bot commented Aug 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 80b9998

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

This PR includes changesets to release 1 package
Name Type
@portabletext/react-native Major

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

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