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

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

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

    Keep the render window small unless you need more adjacent pages mounted.

    The default windowSize is 3, which mounts the previous, current, and next items. Larger windows can make adjacent pages feel more ready, but they also increase render, memory, and image decode work. The library does not cap windowSize; choosing a large value is an explicit performance tradeoff.

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

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