Keyed Siblings in Dynamic Lists
When you build a dynamic element list (Vec<Element>) in Dioxus, every sibling in that list must follow the same keying strategy — either all keyed or all non-keyed. Mixing them causes a runtime panic: all siblings must be keyed or all siblings must be non-keyed.
The Problem
Section titled “The Problem”The Pagination component builds a list of buttons and ellipsis separators. Buttons have keys, but the ellipsis span did not:
let mut elements: Vec<Element> = vec![];
for &p in &pages { if let Some(prev_p) = prev { if p > prev_p + 1 { // ❌ Non-keyed sibling among keyed siblings elements.push(rsx! { span { class: "px-2 text-sm text-muted-foreground", "…" } }); } }
elements.push(rsx! { Button { key: "{p}", // Keyed variant: ButtonVariant::Outline, onclick: move |_| on_change(p), "{p + 1}" } }); prev = Some(p);}Clicking pagination buttons or searching contacts triggers a re-render. Dioxus’s reconciler detects the mixed keying and panics.
The Fix
Section titled “The Fix”Add a key attribute to the ellipsis span with a unique, prefixed value:
let mut elements: Vec<Element> = vec![];
for &p in &pages { if let Some(prev_p) = prev { if p > prev_p + 1 { // ✅ Keyed with prefix to avoid collision with button keys elements.push(rsx! { span { key: "ellipsis-{prev_p}", class: "px-2 text-sm text-muted-foreground", "…" } }); } }
elements.push(rsx! { Button { key: "{p}", variant: ButtonVariant::Outline, onclick: move |_| on_change(p), "{p + 1}" } }); prev = Some(p);}The "ellipsis-{prev_p}" prefix prevents collision with the numeric button keys ("0", "1", etc.) while keeping each ellipsis uniquely identified by its position in the page sequence.
The Rule
Section titled “The Rule”In any dynamic list — loops, Vec<Element>, or conditional renders that produce sibling nodes:
- If any sibling has a
key, every sibling must have akey. - Keys must be unique among siblings (not globally unique).
- Use string prefixes to prevent collisions between different element types.
// ✅ Correct: all siblings keyed with unique prefixed valuesfor item in items { if show_separator { elements.push(rsx! { span { key: "sep-{item.id}", "•" } }); } elements.push(rsx! { ItemCard { key: "item-{item.id}", data: item } });}Key Files
Section titled “Key Files”| File | Role |
|---|---|
src/ui/pagination_ui.rs | Pagination component with the keyed ellipsis fix |
src/mods/contact/views/contact_list_view.rs | Primary consumer — contact list with page navigation |