Skip to content

Allow per-jump animation control for TabView / Material Top Tabs #13095

@tian000

Description

@tian000

Current behavior

react-native-tab-view exposes animationEnabled as a global boolean. This is enough to always animate or never animate tab changes, but it doesn't let a custom tab bar decide that a specific jumpTo call should skip animation.

The underlying native primitive, react-native-pager-view, already supports both behaviors with setPage(index) and setPageWithoutAnimation(index). PagerViewAdapter currently selects between them based on the global animationEnabled prop.

This comes up in apps with heavy top-level tabs and a custom tab bar. A normal tab press should still animate, but if the user rapidly presses between tabs, the second/third press should be able to cancel or skip the in-flight animation and jump immediately to the requested tab. The current public API doesn't provide a way to do that without either:

  • setting animationEnabled: false for all tab presses/programmatic changes, or
  • patching PagerViewAdapter internally to use setPageWithoutAnimation conditionally.

Related prior issues:

Issue #1371 / PR #1388 solved the coarse version of this by adding animationEnabled. This request is for the next layer: per-jump animation policy.

Expected behavior

Would maintainers be open to a patch that allows jumpTo to opt out of animation for a specific tab change, while preserving current behavior by default?

One possible API shape:

type JumpToOptions = {
  animated?: boolean;
};

type SceneRendererProps = {
  // Backwards compatible: existing `jumpTo(key)` calls keep using `animationEnabled`.
  jumpTo: (key: string, options?: JumpToOptions) => void;
};

Then a custom tab bar could do something like:

const isRapidPress = Date.now() - lastTabPressAt.current < 250;
lastTabPressAt.current = Date.now();

jumpTo(route.key, { animated: !isRapidPress });

Internally, PagerViewAdapter could choose between setPage(index) and setPageWithoutAnimation(index) based on options?.animated ?? animationEnabled. The web / pan responder adapter could use the same optional argument to choose whether to animate the jump.

This would avoid app-level patches while keeping the existing global animationEnabled prop as the default policy.

I am happy to put together a PR if this API direction seems acceptable. If maintainers would prefer a different shape, such as an animationEnabled callback or a shouldAnimateJump(fromIndex, toIndex) prop, I'm glad to adapt before implementing.

Reproduction

https://github.com/tian000/react-navigation-tabview-rapid-jump-repro

This is a minimal Expo repro using the latest @react-navigation/native, @react-navigation/material-top-tabs, and react-native-tab-view versions currently published on npm.

The repro renders two sections:

  1. react-native-tab-view: rapid tab presses are detected, but the custom tab bar can only call jumpTo(route.key). The desired jumpTo(route.key, { animated: !isRapidPress }) API is shown in a code comment.
  2. direct react-native-pager-view: rapid tab presses call setPageWithoutAnimation; normal tab presses call setPage, demonstrating that the lower-level primitive supports the behavior.

Run:

npm install
npm run android

or:

npm install
npm run ios

Then rapidly press Home, Trade, and Explore in both sections.

Platform

  • Android
  • iOS
  • Web
  • Windows
  • MacOS

Packages

  • '@react-navigation/material-top-tabs'
  • 'react-native-tab-view'

Environment

Verified with the repro app:

package version
@react-navigation/native 7.2.2
@react-navigation/material-top-tabs 7.4.24
react-native-tab-view 4.3.0
react-native-pager-view 6.9.1
react-native 0.81.5
expo 54.0.33

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions