Skip to content

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 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.

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.

In any dynamic list — loops, Vec<Element>, or conditional renders that produce sibling nodes:

  1. If any sibling has a key, every sibling must have a key.
  2. Keys must be unique among siblings (not globally unique).
  3. Use string prefixes to prevent collisions between different element types.
// ✅ Correct: all siblings keyed with unique prefixed values
for item in items {
if show_separator {
elements.push(rsx! {
span { key: "sep-{item.id}", "" }
});
}
elements.push(rsx! {
ItemCard { key: "item-{item.id}", data: item }
});
}
FileRole
src/ui/pagination_ui.rsPagination component with the keyed ellipsis fix
src/mods/contact/views/contact_list_view.rsPrimary consumer — contact list with page navigation