feat: add RecycleList virtualized list component#207
feat: add RecycleList virtualized list component#207haywoodfu wants to merge 5 commits intoDioxusLabs:mainfrom
Conversation
Add a new RecycleList component that virtualizes large lists by rendering only the visible slice plus a configurable buffer. Supports dynamic row heights and both container-scroll and window-scroll modes. Primitive (dioxus-primitives): - RecycleList with configurable items, buffer, render_item callback - Spread attributes support via attributes: Vec<Attribute> - Scroll tracking via document::eval() JS bridge (no extra deps) - Automatic container-scroll vs window-scroll detection Preview: - Styled wrapper with shadcn theme - Demo variant with 2000 dynamic-height rows - Component metadata (component.json, docs.md) Tests: - Playwright E2E test for virtualization behavior Closes DioxusLabs#203
|
Preview available at https://dioxuslabs.github.io/components/pr-preview/pr-207/ |
ealmloff
left a comment
There was a problem hiding this comment.
This is a great start, thanks for working on this! When moving the scrollbar, the cursor can move faster than the scrollable content appears:
Screen.Recording.2026-03-02.at.8.23.26.AM.mov
There are also some accessibility considerations for virtual lists. Since not all elements accessible on the page are visible in the dom, they will not show up in screen readers. We need to set special aria attributes to make sure the item count in the list is calculated based on the virtual instead of physical size. Some resourecs:
- React aria handles this well: https://react-aria.adobe.com/Virtualizer
- This is one of the attributes we need for non-interactive lists: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-setsize
| /// .collect(); | ||
| /// | ||
| /// rsx! { | ||
| /// {RecycleList(RecycleListProps { |
There was a problem hiding this comment.
Components shouldn't be called directly as functions. If RecycleList contains hooks, calling it directly would mean it has to act like a hook and only be called when hooks could be called (no conditionals, loops, etc).
It may not be an issue in this example in practice, but I would like to avoid showing examples like this to avoid confusion.
You can use the component directly like a struct instead:
RecycleList { item: ..., ... }We may need to rework the api since borrowed props are not supported. Instead of taking items and passing that through to the closure, can we instead take a item count and just pass the index to the render closure?
RecycleList {
count: 2000,
buffer: 8,
render_item: move |idx| rsx! { article { key: "{idx}", "Row {idx}" } },
}
Add a new RecycleList component that virtualizes large lists by rendering only the visible slice plus a configurable buffer. Supports dynamic row heights and both container-scroll and window-scroll modes.
Primitive (dioxus-primitives):
Preview:
Tests:
Closes #203