Skip to content

Render Select options in chunks as the list scrolls#14663

Open
nessai1 wants to merge 1 commit into
gitbutlerapp:masterfrom
nessai1:select-chunked-option-rendering
Open

Render Select options in chunks as the list scrolls#14663
nessai1 wants to merge 1 commit into
gitbutlerapp:masterfrom
nessai1:select-chunked-option-rendering

Conversation

@nessai1

@nessai1 nessai1 commented Jul 7, 2026

Copy link
Copy Markdown

Problem

Select renders every filtered option eagerly. With a small option list that's fine, but the target-branch selector during project setup (ProjectSetupTarget.svelte) feeds it all remote branches of the repository. On a large monorepo clone (25,261 remote branches, ~600k commits) this means ~25k DOM nodes created in one shot when the dropdown opens — and re-created on every search keystroke.

Measured on an M-series MacBook against a Storybook story with 25,000 options:

Metric Before After
Click → options visible ~13,300 ms ~80 ms
Typing a search query (11 chars) ~80,000 ms ~85 ms
DOM nodes after open ~25,000+ ~1,300

In the real app the freeze is long enough that onboarding a large repo looks like a hang.

Fix

Render options in chunks of 100:

  • renderedOptions = filteredOptions.slice(0, renderLimit) — grouping and keyboard navigation derive from the rendered window;
  • scrolling near the bottom of the dropdown (200 px threshold) grows the window by another chunk, via the existing onscroll hook of ScrollableContainer;
  • changing the search term resets the window back to the first chunk.

No API changes; small lists (< 100 options) behave exactly as before. Since the component already has no scroll-into-view for keyboard highlight, chunking does not regress keyboard behavior: navigation cycles within the rendered window.

Repro / testing

  • Added a Huge List story (25k options) to Select.stories.svelte — open the dropdown before/after this change to feel the difference.
  • Verified in Storybook with Playwright: open latency, chunked loading on scroll, search narrowing + window reset, mouse and keyboard selection.
  • svelte-check and eslint pass on the touched files.

To reproduce the original pain end-to-end: any repo with tens of thousands of remote branches (git config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*' against a big shared monorepo does it) — then add the project in GitButler and open the target-branch dropdown.

🤖 Generated with Claude Code

Opening a Select with tens of thousands of options (e.g. picking a
target branch in a repo with ~25k remote branches) rendered every
option eagerly, freezing the UI for ~13s on open and making the
search input unusable (~80s to type a query). Render the first 100
options and grow the window as the dropdown scrolls; reset the
window when the search term changes.

With the new 25k-option story, opening the dropdown drops from
~13.3s to ~80ms and search typing from ~80s to ~85ms.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Byron Byron added the feedback requested Feedback was requested to help resolve the issue label Jul 9, 2026
@Byron

Byron commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Thanks a lot for giving this a go!

As virtual/chunked rendering is inherently difficult to get right (also across browsers), especially when constants are involved, would you be able to provide before/after videos showing off the fix?

Thanks again lot for your help!

@nessai1

nessai1 commented Jul 9, 2026

Copy link
Copy Markdown
Author

Here are the before/after videos, recorded against a real repo with ~25k remote branches :)

Before (master):

  • Release build: opening the dropdown freezes the UI for ~60-90s, typing in the search field freezes the app hard - each keystroke locks the UI for a long stretch, so entering even a short query takes on the order of a minute.
  • Dev build: the freeze is so much worse (presumably Svelte dev-mode overhead) that the dropdown never visibly opens - the app just hangs on click.
master.mp4
release.mp4

After (this branch), same repo, same builds:

  • Dropdown opens instantly, search is responsive, scrolling loads options in chunks with no blank gaps, and selecting an option works as before.
  • Selecting an option deep in the list works as before.
fix.mp4

@Byron

Byron commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Thanks so much for bearing with me: These results are impressive and I see how much they're needed now.
I will do my best to get them reviewed.

@Byron Byron removed the feedback requested Feedback was requested to help resolve the issue label Jul 10, 2026
@Byron Byron requested a review from Caleb-T-Owens July 10, 2026 03:23
@mtsgrd

mtsgrd commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Great contribution, thank you! I might still have a preference for moving LazyList.svelte into packages/ui and reuse here. I'll pick this up again in the evening. 🙏

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.

3 participants