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
- Render a chat-style list with
maintainScrollAtEnd={{ animated: true }} and maintainScrollAtEndThreshold={0.2}.
- Append text to the last item faster than ~2×/sec (e.g. streaming LLM tokens flushed every ~200ms).
- 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.
Summary
maintainScrollAtEndre-pins to the end at most once per a hardcoded 500ms window whenanimated: true. For fast-updating content (streaming AI chat, live logs) that is too slow to keep up — the list drifts pastmaintainScrollAtEndThreshold, the at-end gate flips tofalse, and following stops mid-stream. Exposing this delay as configuration would make animatedmaintainScrollAtEndusable for streaming UIs.Where
src/core/doMaintainScrollAtEnd.ts— after each animatedscrollToEnd, the re-entrancy lockstate.maintainingScrollAtEndis released by:While
maintainingScrollAtEndistrue, new growth is ignored (theif (!state.maintainingScrollAtEnd)guard), so withanimated: truethe list can re-pin at most every 500ms.Failure mode
maintainScrollAtEnd={{ animated: true }}andmaintainScrollAtEndThreshold={0.2}.scrollToEnd(native ~250ms) can't keep up because re-pinning is blocked for 500ms. Content drifts past the threshold,isWithinMaintainScrollAtEndThresholdbecomesfalse, 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-packagethe500down to150(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 transpileddistfile is fragile across upgrades.Proposed API
Expose the lockout on the options object, defaulting to the current
500(no behavior change):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/list3.0.4Thanks for the library — the recent 3.0.x append-flow fixes are much appreciated.