Repository navigation
streamingAnimation: add a gradient option for the last characters of streamed text #904
Description
👋 Hi from @doctolib! We need this for our product, and we'd love to help make it happen.
| 🏢 Who | @doctolib |
| 🎯 Why | Product need |
| 💬 Ping us | @arnaud-zg, @bacarybruno |
Thanks for building such a great library! 🙏
TL;DR
When text streams in, we want the newest characters to fade out like a gradient (solid color → invisible).
Today streamingAnimation can only fade the whole new chunk at once. Can we add a gradient option?
What we have vs. what the library does
Our renderer today: the last 15 characters get a color that goes from blue to transparent.
H e l l o w o r l d
█ ▓ ▓ ▒ ░ ← each letter a bit more transparent
└ last 15 ┘
streamingAnimation today: the new chunk appears all at once, going from invisible to visible.
H e l l o [w o r l d]
░ ░ ░ ░ ░ t = 0ms (invisible)
▒ ▒ ▒ ▒ ▒ t = 100ms (half visible)
█ █ █ █ █ t = 200ms (fully visible)
| Our renderer | streamingAnimation |
|
|---|---|---|
| What changes | Color, letter by letter | Opacity, same for every letter |
| Based on | Position in the text | Time |
| iOS | n/a | one CADisplayLink, ~200ms |
| Android | n/a | one ValueAnimator, ~150ms |
| Can we configure it? | Yes (length, colors) | No |
A problem with fast updates
When text arrives very fast (one update per frame, ~16ms), the fade has no time to play:
sequenceDiagram
participant App as App (new text)
participant Fade as Native fade
App->>Fade: update 1: start fading chunk A (needs 200ms)
Note over Fade: only 16ms pass
App->>Fade: update 2
Fade-->>Fade: stop fade A, show A fully right away
Fade->>Fade: start fading chunk B
Note over Fade: same thing every frame, so you never see a fade
Each call to animate(...) cancels the previous fade and jumps it to fully visible (alpha 1).
Workaround today: group updates together and send them every ~150 to 200ms (the fade duration). The fade shows up again, but the text feels slower.
Proposal
Add a gradient type. The color of each letter depends on how far it is from the end of the text:
streamingAnimation={{
type: 'gradient', // 'fade' stays the default, nothing breaks
startColor: '#107ACA', // color of older letters
endColor: 'transparent', // color of the newest letter
tailLength: 15, // how many letters the gradient covers
}}flowchart LR
U[New text arrives] --> T{type?}
T -- fade --> F[Today: whole new chunk<br/>goes from invisible to visible]
T -- gradient --> G[New: last 15 letters get<br/>a color from start to end]
G --> R[The gradient follows the end of the text.<br/>No timer, so fast updates work fine]
Why this helps:
- It looks like the "typewriter" effect most chat apps use.
- It depends on position, not time, so it works even when text updates every frame.
- It does not break anything:
fadestays the default.
If a built-in option is too much: a styling hook would also work, for example
getStreamingStyle(indexFromEnd) => ({ color, opacity }).
Questions
- Would you accept a gradient option in
streamingAnimation(built-in or as a hook)? - Should the "fast updates cancel the fade" problem be its own issue?
Happy to share the exact source code references and to help implement it.
Activity
eszlamczyk commentedon Sep 30, 2026
Hi @arnaud-zg thanks for the feature request. As a heads up, we wont be able to fit this into next release (tomorrow) so this would be a
1.2.0feature, hope thats not a big issue for you guys (In case of need there is always option to pin down specificnightlyversion).This looks like a cool feature to have and we will definitely try implementing this, but our architecture/API needs further discussion, I'll keep you guys informed if we will have some advancements.
arnaud-zg commentedon Oct 2, 2026
Thanks @eszlamczyk, 1.2.0 works for us. It would be great to have a nightly versionwe could test in advance.
We'll test it on our side. 🙏