Skip to content

streamingAnimation: add a gradient option for the last characters of streamed text #904

Open

Description

@arnaud-zg

👋 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
Loading

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]
Loading

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: fade stays the default.

If a built-in option is too much: a styling hook would also work, for example
getStreamingStyle(indexFromEnd) => ({ color, opacity }).

Questions

  1. Would you accept a gradient option in streamingAnimation (built-in or as a hook)?
  2. 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

  1. eszlamczyk commented on Sep 30, 2026

    @eszlamczyk
    Collaborator

    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.0 feature, hope thats not a big issue for you guys (In case of need there is always option to pin down specific nightly version).

    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.

  2. self-assigned this
    on Sep 30, 2026
  3. arnaud-zg commented on Oct 2, 2026

    @arnaud-zg
    Author

    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.0 feature, hope thats not a big issue for you guys (In case of need there is always option to pin down specific nightly version).

    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.

    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. 🙏

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions