docs/solutions/performance-issues/2026-04-04-flattened-list-rendering-must-not-wrap-every-item-in-its-own-list-container.md
The standalone editor benchmark still had a large red list-heavy mount lane after the mark-bundle fix.
The key question was whether the cost came from:
ListPluginDedicated list rows in the standalone lab after the kept fix:
49_mount-10k-list-markdown: Plate 934.20 ms, Slate 619.50 ms96_mount-10k-list-core: Plate 642.80 ms, Slate 586.30 ms97_mount-10k-list-only: Plate 871.90 ms, Slate 563.70 msThat split means the old bill was ListPlugin, not the flattened list payload
by itself.
list-core lower
bound is much cheaper.belowNodes wrappers. That also did
not survive measurement cleanly enough to keep.First, add the two missing benchmark rows that isolate the seam:
96_mount-10k-list-core97_mount-10k-list-onlyThen inspect the rendered DOM, not just timings.
The DOM probe showed the original bad shape:
list-core: 0 <ul>, 0 <li>, 30,000 paragraph nodeslist-only: 30,000 <ul>, 30,000 <li>, 30,000 paragraph nodes10,000 <ul>, 30,000 <li>So the old Plate list render model was paying one list container per item instead of one logical list container or a lighter paragraph-level list-item render.
The kept fix is:
belowNodespipeRenderElement(...) keeps the plain element fast path when inject props
are pathless and no wrapper is active for the current elementThat removes unordered wrapper DOM completely:
list-only: 0 <ul>, 0 <li>, 30,000
[role="listitem"] paragraphsThis is why the list-only lane dropped from about 1564 ms to about 872 ms,
and the full list-heavy markdown lane dropped from about 1452 ms to about
934 ms.