Deft
Ask AI

Search

Search skills, authors and topics

vercel-labs / agent-skills

vercel-react-native-skills

React Native and Expo best practices for building performant mobile apps. Use when building React Native components, optimizing list performance, implementing animations, or working with native modules. Triggers on tasks involving React Native, Expo, mobile performance, or native platform APIs.

Install

npx skills add https://github.com/vercel-labs/agent-skills --skill vercel-react-native-skills
title
Never Track Scroll Position in useState
impact
HIGH
impactDescription
prevents render thrashing during scroll
tags
scroll, performance, reanimated, useRef

Never Track Scroll Position in useState

Never store scroll position in useState. Scroll events fire rapidly—state updates cause render thrashing and dropped frames. Use a Reanimated shared value for animations or a ref for non-reactive tracking.

Incorrect (useState causes jank):

tsx
import { useState } from 'react'import {  ScrollView,  NativeSyntheticEvent,  NativeScrollEvent,} from 'react-native'
function Feed() {  const [scrollY, setScrollY] = useState(0)
  const onScroll = (e: NativeSyntheticEvent<NativeScrollEvent>) => {    setScrollY(e.nativeEvent.contentOffset.y) // re-renders on every frame  }
  return <ScrollView onScroll={onScroll} scrollEventThrottle={16} />}

Correct (Reanimated for animations):

tsx
import Animated, {  useSharedValue,  useAnimatedScrollHandler,} from 'react-native-reanimated'
function Feed() {  const scrollY = useSharedValue(0)
  const onScroll = useAnimatedScrollHandler({    onScroll: (e) => {      scrollY.value = e.contentOffset.y // runs on UI thread, no re-render    },  })
  return (    <Animated.ScrollView      onScroll={onScroll}      // higher number has better performance, but it fires less often.      // unset this if you need higher precision over performance.      scrollEventThrottle={16}    />  )}

Correct (ref for non-reactive tracking):

tsx
import { useRef } from 'react'import {  ScrollView,  NativeSyntheticEvent,  NativeScrollEvent,} from 'react-native'
function Feed() {  const scrollY = useRef(0)
  const onScroll = (e: NativeSyntheticEvent<NativeScrollEvent>) => {    scrollY.current = e.nativeEvent.contentOffset.y // no re-render  }
  return <ScrollView onScroll={onScroll} scrollEventThrottle={16} />}