Skip to content

feat: add RecycleList virtualized list component#207

Open
haywoodfu wants to merge 5 commits intoDioxusLabs:mainfrom
haywoodfu:feat/recycle-list
Open

feat: add RecycleList virtualized list component#207
haywoodfu wants to merge 5 commits intoDioxusLabs:mainfrom
haywoodfu:feat/recycle-list

Conversation

@haywoodfu
Copy link
Contributor

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
  • 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 #203

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
@github-actions
Copy link

Copy link
Member

@ealmloff ealmloff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

/// .collect();
///
/// rsx! {
/// {RecycleList(RecycleListProps {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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}" } },
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add RecycleList (virtualized list)

2 participants