Skip to content

fix: update sibling_slot immediately on component reuse#4093

Open
Madoshakalaka wants to merge 1 commit intomasterfrom
fix/insert-before-crash
Open

fix: update sibling_slot immediately on component reuse#4093
Madoshakalaka wants to merge 1 commit intomasterfrom
fix/insert-before-crash

Conversation

@Madoshakalaka
Copy link
Copy Markdown
Member

@Madoshakalaka Madoshakalaka commented Mar 30, 2026

Fixes #4092

Impacted usage patterns

The bug requires three children in a sibling list, left to right:

  1. A child that transitions from no DOM output to some
  2. A component rendering zero DOM nodes
  3. A child whose DOM node is removed in the current render
Opposing conditionals with an empty component between them
if *toggled { <span/> }
<Empty/>
if !*toggled { <div>{"old"}</div> }
State-driven show/hide swap
if *state == View::New { <NewPanel/> }
<Empty/>
if *state == View::Old { <OldPanel/> }
Empty list gaining items while a sibling disappears
{ for items.iter().map(|i| html!{ <span>{i}</span> }) }
<Empty/>
if show_footer { <footer/> }
Multiple empty components
if *toggled { <span/> }
<EmptyA/>
<EmptyB/>
if !*toggled { <div>{"old"}</div> }

In all cases, the "empty component" can be anything whose rendered output produces zero DOM nodes: html! {}, html! { if false { <div/> } }, or a component wrapping another empty component.

Checklist

  • I have reviewed my own code
  • I have added tests

During parent reconciliation, BComp::reconcile deferred the
sibling_slot update to the scheduler via PropsUpdateRunner. The
returned own_position chains through sibling_slot, so for components
with empty output the stale reference could point to a node already
detached earlier in the same reconciliation pass, causing insertBefore
to fail with NotFoundError.

Reassign sibling_slot synchronously in Scope::reuse before scheduling
the props update so the slot chain resolves correctly while the parent
is still reconciling.
@github-actions
Copy link
Copy Markdown

Benchmark - core

Yew Master

vnode           fastest       │ slowest       │ median        │ mean          │ samples │ iters
╰─ vnode_clone  2.126 ns      │ 3.436 ns      │ 2.129 ns      │ 2.235 ns      │ 100     │ 1000000000

Pull Request

vnode           fastest       │ slowest       │ median        │ mean          │ samples │ iters
╰─ vnode_clone  2.126 ns      │ 3.375 ns      │ 2.129 ns      │ 2.156 ns      │ 100     │ 1000000000

@github-actions
Copy link
Copy Markdown

Visit the preview URL for this PR (updated for commit 3c9adc7):

https://yew-rs-api--pr4093-fix-insert-before-cr-gzekan40.web.app

(expires Mon, 06 Apr 2026 11:02:42 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

@github-actions
Copy link
Copy Markdown

Size Comparison

Details
examples master (KB) pull request (KB) diff (KB) diff (%)
async_clock 100.818 100.818 0 0.000%
boids 168.448 168.665 +0.217 +0.129%
communication_child_to_parent 94.076 94.200 +0.124 +0.132%
communication_grandchild_with_grandparent 105.914 106.194 +0.280 +0.265%
communication_grandparent_to_grandchild 102.257 102.539 +0.282 +0.276%
communication_parent_to_child 91.486 91.610 +0.124 +0.136%
contexts 105.977 106.447 +0.471 +0.444%
counter 86.798 86.798 0 0.000%
counter_functional 88.834 88.834 0 0.000%
dyn_create_destroy_apps 90.713 90.713 0 0.000%
file_upload 99.812 99.812 0 0.000%
function_delayed_input 94.807 94.807 0 0.000%
function_memory_game 173.664 174.198 +0.534 +0.308%
function_router 395.375 396.925 +1.550 +0.392%
function_todomvc 164.955 165.362 +0.407 +0.247%
futures 235.551 235.551 0 0.000%
game_of_life 105.099 105.099 0 0.000%
immutable 259.314 259.927 +0.612 +0.236%
inner_html 81.341 81.341 0 0.000%
js_callback 109.957 110.301 +0.344 +0.313%
keyed_list 180.407 180.532 +0.125 +0.069%
mount_point 84.714 84.714 0 0.000%
nested_list 113.660 113.940 +0.280 +0.247%
node_refs 92.087 92.211 +0.124 +0.135%
password_strength 1719.252 1719.376 +0.124 +0.007%
portals 93.560 93.684 +0.124 +0.133%
router 366.017 367.248 +1.231 +0.336%
suspense 113.962 114.433 +0.471 +0.413%
timer 88.943 88.943 0 0.000%
timer_functional 99.368 99.519 +0.150 +0.151%
todomvc 142.661 142.661 0 0.000%
two_apps 86.711 86.711 0 0.000%
web_worker_fib 136.457 136.737 +0.280 +0.205%
web_worker_prime 187.640 187.920 +0.280 +0.149%
webgl 83.486 83.486 0 0.000%

✅ None of the examples has changed their size significantly.

@Madoshakalaka Madoshakalaka marked this pull request as ready for review March 30, 2026 11:17
@github-actions
Copy link
Copy Markdown

Benchmark - SSR

Yew Master

Details
Benchmark Round Min (ms) Max (ms) Mean (ms) Standard Deviation
Baseline 10 310.917 311.643 311.290 0.232
Hello World 10 483.256 517.385 490.767 9.894
Function Router 10 35414.938 39281.304 36807.140 1213.993
Concurrent Task 10 1006.042 1007.536 1006.727 0.549
Many Providers 10 1111.340 1140.981 1124.389 10.814

Pull Request

Details
Benchmark Round Min (ms) Max (ms) Mean (ms) Standard Deviation
Baseline 10 310.939 311.629 311.206 0.221
Hello World 10 468.545 486.141 473.174 5.645
Function Router 10 33716.661 35806.636 34366.594 671.805
Concurrent Task 10 1005.772 1007.439 1006.825 0.619
Many Providers 10 1099.961 1168.814 1111.735 20.623

@finnbear
Copy link
Copy Markdown
Contributor

Thanks so much for the quick investigation and fix!

I can confirm this fixes my app:

[patch.crates-io]
yew = { git = "https://github.com/yewstack/yew.git", branch = "fix/insert-before-crash" }

@Madoshakalaka Madoshakalaka added the A-yew Area: The main yew crate label Mar 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-yew Area: The main yew crate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0.21 -> 0.22 regression: failed to insert node before next sibling NotFoundError

2 participants