For AI agents: the complete documentation index is available at /2.x/llms.txt, the full documentation bundle is available at /2.x/llms-full.txt, and this page is available as Markdown at /2.x/guide/performance-optimization-tips.md.
v3 is stable. See what changed
  • English
  • 2.x
  • Performance Optimization Tips

    Wrap the renderItem function with useCallback to prevent unnecessary re-renders.

    import { useCallback, useState } from 'react'; 
    import { ScrollView, Image, Modal } from 'react-native';
    import { GestureViewer } from 'react-native-gesture-image-viewer';
    
    const images = [...];
    
    function App() {
      const [visible, setVisible] = useState(false);
    
      const renderImage = useCallback((imageUrl: string) => {
        return <Image source={{ uri: imageUrl }} style={{ width: '100%', height: '100%' }} resizeMode="contain" />;
      }, []);
    
      return (
        <Modal visible={visible} onRequestClose={() => setVisible(false)}>
          <GestureViewer
            data={images}
            renderItem={renderImage}
            ListComponent={ScrollView}
            onDismiss={() => setVisible(false)}
          />
        </Modal>
      );
    }

    Avoid rebuilding unchanged data.

    Passing a new array is supported and is the correct way to publish list changes. In a parent that rerenders frequently, however, rebuilding an equivalent array can repeat viewer and dimension-reconciliation work. Pass the existing array when no transformation is needed, and memoize only genuinely derived data.

    import { useMemo } from 'react';
    
    // ❌ Avoid: this creates a new array on every render.
    <GestureViewer data={[...images]} renderItem={renderImage} />
    
    // ✅ Prefer: reuse the existing prop or state value when its contents are unchanged.
    <GestureViewer data={images} renderItem={renderImage} />
    
    // ✅ If a transformation is required, memoize the derived array from its source.
    function LimitedGallery({ images }: { images: string[] }) {
      const visibleImages = useMemo(
        () => images.slice(0, 20),
        [images],
      );
    
      return <GestureViewer data={visibleImages} renderItem={renderImage} />;
    }

    Do not mutate data in place. Replace the array when its contents change:

    // ❌ Avoid
    images.push(nextImage);
    setImages(images);
    
    // ✅ Prefer
    setImages((currentImages) => [...currentImages, nextImage]);

    For large images, we recommend using expo-image or FastImage.

    import { useCallback, useState } from 'react';
    import { ScrollView, Modal } from 'react-native';
    import { GestureViewer } from 'react-native-gesture-image-viewer';
    import { Image } from 'expo-image'; 
    
    const images = [...];
    
    function App() {
      const [visible, setVisible] = useState(false);
    
      const renderImage = useCallback((imageUrl: string) => {
        return (
          <Image
            source={{ uri: imageUrl }}
            style={{ width: '100%', height: '100%' }}
            contentFit="contain"
          />
        ); 
      }, []);
      return (
        <Modal visible={visible} onRequestClose={() => setVisible(false)}>
          <GestureViewer
            data={images}
            renderItem={renderImage}
            ListComponent={ScrollView}
            onDismiss={() => setVisible(false)}
          />
        </Modal>
      );
    }

    For handling many images, we recommend using FlashList.

    import { useCallback, useState } from 'react';
    import { Image, Modal } from 'react-native';
    import { GestureViewer } from 'react-native-gesture-image-viewer';
    import { FlashList } from '@shopify/flash-list'; 
    
    const images = [...];
    
    function App() {
      const [visible, setVisible] = useState(false);
    
      const renderImage = useCallback((imageUrl: string) => {
        return <Image source={{ uri: imageUrl }} style={{ width: '100%', height: '100%' }} resizeMode="contain" />;
      }, []);
    
      return (
        <Modal visible={visible} onRequestClose={() => setVisible(false)}>
          <GestureViewer
            data={images}
            renderItem={renderImage}
            ListComponent={FlashList} 
            onDismiss={() => setVisible(false)}
          />
        </Modal>
      );
    }

    Test on actual devices (performance may be limited in simulators).