Skip to content

Make the maintainScrollAtEnd re-pin delay configurable (hardcoded 500ms can't keep up with streaming) #470

Description

@Xelson

Summary

maintainScrollAtEnd re-pins to the end at most once per a hardcoded 500ms window when animated: true. For fast-updating content (streaming AI chat, live logs) that is too slow to keep up — the list drifts past maintainScrollAtEndThreshold, the at-end gate flips to false, and following stops mid-stream. Exposing this delay as configuration would make animated maintainScrollAtEnd usable for streaming UIs.

Where

src/core/doMaintainScrollAtEnd.ts — after each animated scrollToEnd, the re-entrancy lock state.maintainingScrollAtEnd is released by:

setTimeout(
  () => { state.maintainingScrollAtEnd = false; },
  maintainScrollAtEnd.animated ? 500 : 0,
);

While maintainingScrollAtEnd is true, new growth is ignored (the if (!state.maintainingScrollAtEnd) guard), so with animated: true the list can re-pin at most every 500ms.

Failure mode

  1. Render a chat-style list with maintainScrollAtEnd={{ animated: true }} and maintainScrollAtEndThreshold={0.2}.
  2. Append text to the last item faster than ~2×/sec (e.g. streaming LLM tokens flushed every ~200ms).
  3. The animated scrollToEnd (native ~250ms) can't keep up because re-pinning is blocked for 500ms. Content drifts past the threshold, isWithinMaintainScrollAtEndThreshold becomes false, and following stops — the new text streams off-screen until the next pin. Visually: a few large animated catch-up jumps with line-by-line drift in between, instead of a smooth follow.

The non-animated path (animated: false, 0ms lockout) keeps up perfectly but loses the animation.

Workaround

We currently patch-package the 500 down to 150 (just under our ~200ms token-flush interval). Animated follow then re-pins on every flush and keeps up smoothly. It works well, but patching a transpiled dist file is fragile across upgrades.

Proposed API

Expose the lockout on the options object, defaulting to the current 500 (no behavior change):

maintainScrollAtEnd={{ animated: true, repinDelayMs: 150 }}

Any name/shape is fine — the ask is just a knob for the re-pin lockout so streaming/chat UIs can tune how aggressively the list re-pins while animating.

Environment

  • @legendapp/list 3.0.4
  • React Native (new architecture), iOS

Thanks for the library — the recent 3.0.x append-flow fixes are much appreciated.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions