npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@thatkid02/react-native-pdf-viewer

v0.0.8

Published

Pdf viewer with page thumbnails, page navigation, and zooming capabilities for React Native apps.

Readme

react-native-pdf-viewer

npm version npm downloads

High-performance PDF viewer for React Native, built with Nitro Modules for native rendering.

Questions before you start

  • Ask AI here - https://deepwiki.com/thatkid02/react-native-pdf-viewer

Features

  • 📄 Native PDF rendering — PDFKit (iOS) & PdfRenderer (Android)
  • 🔍 Pinch-to-zoom & pan — Smooth gesture handling
  • 🖼️ Thumbnail generation — Async page previews
  • 🌐 Local & remote filesfile://, http://, https://
  • 📱 iOS layouts — Horizontal scroll & paging modes
  • 💾 Memory efficient — Virtualized pages, LRU cache

Installation

npm install @thatkid02/react-native-pdf-viewer react-native-nitro-modules

iOS

cd ios && pod install

⚠️ Important: If Installation Fails

If you encounter issues during installation related to missing nitrogen/generated files, you may need to apply a yarn patch.

Problem: builder bob creates module folder for index with path issue

Solution: Add a yarn patch to lib/module/index.js to handle the missing nitrogen path:

yarn patch @thatkid02/react-native-pdf-viewer

Then for patch modify lib/module/index.js and change this line:

// Before (may fail if nitrogen not generated yet)
const PdfViewerConfig = require('../nitrogen/generated/shared/json/PdfViewerConfig.json');

// After (with proper path resolution)
const PdfViewerConfig = require('../../nitrogen/generated/shared/json/PdfViewerConfig.json');

Key point: The path ../../nitrogen is important because the nitrogen module exists at the repository root, not in the module directory.

After patching, run:

yarn patch-commit -u

Quick Start

import { PdfViewerView } from '@thatkid02/react-native-pdf-viewer';

function App() {
  return (
    <PdfViewerView
      source="https://example.com/document.pdf"
      style={{ flex: 1 }}
      onLoadComplete={(e) => console.log(`Loaded ${e.pageCount} pages`)}
      onError={(e) => console.error(e.message)}
    />
  );
}

Usage with Ref

Callbacks must be wrapped with callback() from nitro-modules to avoid re-renders. Learn more

import { useRef } from 'react';
import { View, Button } from 'react-native';
import { 
  PdfViewerView, 
  callback,
  type PdfViewerRef 
} from '@thatkid02/react-native-pdf-viewer';

function PdfScreen() {
  const pdfRef = useRef<PdfViewerRef>(null);

  return (
    <View style={{ flex: 1 }}>
      <PdfViewerView
        hybridRef={callback((ref) => { pdfRef.current = ref; })}
        source="https://example.com/document.pdf"
        style={{ flex: 1 }}
        enableZoom={true}
        minScale={0.5}
        maxScale={4.0}
        onLoadComplete={callback((e) => {
          console.log(`${e.pageCount} pages, ${e.pageWidth}x${e.pageHeight}`);
        })}
        onPageChange={callback((e) => {
          console.log(`Page ${e.page} of ${e.pageCount}`);
        })}
        onThumbnailGenerated={callback((e) => {
          console.log(`Thumbnail page ${e.page}: ${e.uri}`);
        })}
      />
      
      <View style={{ flexDirection: 'row', padding: 16, gap: 8 }}>
        <Button title="Page 1" onPress={() => pdfRef.current?.goToPage(0)} />
        <Button title="Zoom 2x" onPress={() => pdfRef.current?.setScale(2)} />
        <Button title="Thumbnails" onPress={() => pdfRef.current?.generateAllThumbnails()} />
      </View>
    </View>
  );
}

iOS-Only Features

// Horizontal scrolling
<PdfViewerView source={url} horizontal={true} />

// Paging mode (swipe between pages)
<PdfViewerView source={url} enablePaging={true} />

// Combined
<PdfViewerView source={url} horizontal={true} enablePaging={true} />

Glass UI / Transparent Bars

Content scrolls behind transparent headers/toolbars:

<PdfViewerView
  source={url}
  contentInsetTop={100}    // Header height
  contentInsetBottom={80}  // Toolbar height
  style={{ flex: 1 }}
/>

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | source | string | — | PDF URI (file://, http://, https://) | | spacing | number | 8 | Space between pages (px) | | enableZoom | boolean | true | Enable pinch-to-zoom | | minScale | number | 0.5 | Minimum zoom scale | | maxScale | number | 4.0 | Maximum zoom scale | | showsActivityIndicator | boolean | true | Show loading spinner | | horizontal | boolean | false | Horizontal scroll (iOS only) | | enablePaging | boolean | false | Paging mode (iOS only) | | contentInsetTop | number | 0 | Top inset for glass UI | | contentInsetBottom | number | 0 | Bottom inset for glass UI | | contentInsetLeft | number | 0 | Left inset | | contentInsetRight | number | 0 | Right inset |

Events

| Event | Payload | Description | |-------|---------|-------------| | onLoadComplete | { pageCount, pageWidth, pageHeight } | PDF loaded successfully | | onPageChange | { page, pageCount } | Visible page changed | | onScaleChange | { scale } | Zoom level changed | | onError | { message, code } | Error occurred | | onThumbnailGenerated | { page, uri } | Thumbnail ready | | onLoadingChange | { isLoading } | Loading state changed |

Methods

const pdfRef = useRef<PdfViewerRef>(null);

// Navigate to page (0-indexed)
pdfRef.current?.goToPage(0);

// Set zoom level
pdfRef.current?.setScale(2.0);

// Generate single thumbnail
pdfRef.current?.generateThumbnail(0);

// Generate all thumbnails
pdfRef.current?.generateAllThumbnails();

// Get document info
const info = pdfRef.current?.getDocumentInfo();
// { pageCount, pageWidth, pageHeight, currentPage }

Platform Differences

| Feature | Android | iOS | |---------|:-------:|:---:| | Vertical scroll | ✅ | ✅ | | Horizontal scroll | — | ✅ | | Paging mode | — | ✅ | | Pinch-to-zoom | ✅ | ✅ | | Double-tap zoom | ✅ | ✅ | | Pan when zoomed | ✅ | ✅ | | Thumbnails | ✅ | ✅ | | Remote URLs | ✅ | ✅ | | Local files | ✅ | ✅ |

Thumbnail Caching

Thumbnails are automatically cached on disk and in memory. When you call generateThumbnail():

  1. Memory cache is checked first (instant)
  2. Disk cache is checked next (fast)
  3. Generated on-demand if not cached (async)

This means multiple PdfViewerView instances with the same URL share cached thumbnails:

// Main viewer
<PdfViewerView
  source={pdfUrl}
  hybridRef={callback((ref) => { mainRef.current = ref; })}
  onThumbnailGenerated={callback((e) => {
    // Thumbnail generated by main viewer is cached
    setThumbnails(prev => new Map(prev).set(e.page, e.uri));
  })}
/>

// Carousel - can request thumbnails even before its PDF loads
// Will return cached thumbnails instantly if main viewer already generated them
<PdfViewerView
  source={pdfUrl}  // Same URL = same cache
  hybridRef={callback((ref) => {
    // Request thumbnail - returns from cache if available
    ref?.generateThumbnail(0);
  })}
  onThumbnailGenerated={callback((e) => {
    setCarouselThumbnail(e.uri);
  })}
/>

Note: If generateThumbnail() is called before the document loads, the request is queued and processed automatically once loading completes.

Troubleshooting

PDF fails to load from URL
Ensure the URL is accessible. Use HTTPS in production.

Out of memory on large PDFs
Lower maxScale to reduce memory usage. The viewer automatically manages memory with dynamic quality scaling.

Horizontal/paging not working
These are iOS-only features. Android uses vertical scroll.

Contributing

See CONTRIBUTING.md for development setup.

License

MIT