Bug: `onScroll` no longer triggers on native after updating to Tamagui v2
Summary
After updating to Tamagui v2, onScroll no longer fires on native when using a plain Tamagui ScrollView. The ScrollView still scrolls, but the onScroll callback is never invoked. Wrapping the same ScrollView with reanimated's createAnimatedComponent makes onScroll work again.
We have a screen that relies on onScroll to track scroll position. After upgrading to Tamagui v2, the handler silently stopped firing on native. No error, no warning, the list just scrolls without the callback ever running. After further debugging we narrowed it down to Tamagui's prop handling on native.
Minimal reproduction
Repo: https://github.com/idrakimuhamad/tamagui-onscroll-repro
The repro screen renders two identical ScrollViews side by side:
- Plain Tamagui ScrollView —
styled(TamaguiScrollView, { flex: 1 })with anonScrollhandler - AnimatedScrollView —
Animated.createAnimatedComponent(TamaguiScrollView)with the sameonScrollhandler
Both lists scroll. But:
| ScrollView | Scrolls? | onScroll fires? |
|---|---|---|
Plain Tamagui ScrollView |
✅ | ❌ (counter stays 0) |
AnimatedScrollView (reanimated) |
✅ | ✅ (counter climbs) |
Root cause
I asked my agent to dig, and it found out that Tamagui's styled() core strips onScroll on native. The prop is listed in webPropsToSkip.native.ts:
// node_modules/@tamagui/web/src/helpers/webPropsToSkip.native.ts
export const webPropsToSkip = {
...
onScroll: 1, // <-- stripped on native
...
}Reanimated's createAnimatedComponent re-injects onScroll via its PropsFilter, which is why the wrapped ScrollView works.
Question / reasoning
Why is onScroll in webPropsToSkip for native? onScroll is a legitimate native ScrollView prop AFAIK, it's not web-only. Stripping it on native means any plain Tamagui ScrollView silently loses scroll tracking, which is surprising and hard to debug (no error, just a dead callback).
Is this intentional? If so, what is the supported way to receive onScroll on a plain Tamagui ScrollView on native? Do we need to wrap every scrollable in createAnimatedComponent (which pulls in reanimated as a hard requirement), or should onScroll be removed from the native skip list?
Expected behavior
onScroll should fire on a plain Tamagui ScrollView on native, matching the web behavior. If it must be stripped, there should be a documented, non-reanimated way to opt back in.
Environment
- Tamagui 2.7.6
- Expo SDK 55
- React Native 0.83.2
- react-native-reanimated 4.2.2
- iOS (Expo Go)
Source: tamagui/tamagui