How to View a PDF in React Native Using react-native-pdf

React Native does not ship with a built-in PDF viewer, so developers rely on the react-native-pdf library to render documents from remote URLs, local file paths, or base64 data strings on both iOS and Android.

While the core React Native framework lacks native PDF capabilities, the community package react-native-pdf bridges this gap by wrapping platform-specific APIs—PDFKit on iOS and PdfRenderer on Android—into a declarative React component. According to the react-native-pdf source code, the library exposes a single Pdf component that handles binary decoding, memory management, and gesture recognition, allowing you to view a PDF in React Native using react-native-pdf with minimal native configuration.

Installation and Native Module Linking

Install the package from npm and link the native modules. For React Native 0.60 and above, autolinking handles the native side; for older versions, you must manually link.

npm install --save react-native-pdf

# or

yarn add react-native-pdf

For iOS, install the CocoaPods dependency:

cd ios && pod install && cd ..

For React Native versions below 0.60, run the manual linking command:

npx react-native link react-native-pdf

Basic Implementation with the Pdf Component

The library exports a default Pdf component from src/index.js. Import it and provide a source object containing a uri string.

import React from 'react';
import {StyleSheet, View, Dimensions} from 'react-native';
import Pdf from 'react-native-pdf';

const PdfViewer = () => {
  const source = {
    uri: 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf',
    cache: true, // Cache the file locally on Android
  };

  return (
    <View style={styles.container}>
      <Pdf
        source={source}
        style={styles.pdf}
        onLoadComplete={(pageCount, filePath) => {
          console.log(`PDF loaded – ${pageCount} pages`);
        }}
        onPageChanged={(page, pageCount) => {
          console.log(`Current page: ${page}/${pageCount}`);
        }}
        onError={(error) => {
          console.log('PDF load error:', error);
        }}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  pdf: {
    flex: 1,
    width: Dimensions.get('window').width,
  },
});

Key props:

  • source – An object with a uri property pointing to a remote URL, local file path, or data URI.
  • onLoadComplete – Callback invoked when the PDF finishes loading, receiving pageCount and filePath.
  • onPageChanged – Callback triggered when the user swipes to a new page.

Source Configuration: Remote, Local, and Base64 URIs

The uri string accepts multiple formats, but platform-specific path conventions apply. For local bundled assets, use conditional logic to handle iOS and Android directory structures differently.

const source = {
  uri: Platform.OS === 'ios' 
    ? `${RNFS.MainBundlePath}/sample.pdf` 
    : 'file:///android_asset/sample.pdf',
};

For base64-encoded PDFs, prefix the string with data:application/pdf;base64, and pass it as the uri. On Android, setting cache: true in the source object persists the file to disk, improving reload performance according to the native implementation in android/src/main/java/com/wonday/reactnativepdf/ReactNativePdfView.java.

Advanced Features and Programmatic Control

Beyond basic rendering, the component supports granular control over navigation and presentation:

  • Horizontal paging – Set the horizontal prop to true to display pages side-by-side.
  • Zoom constraints – Use maxScale and minScale props to prevent excessive memory consumption when viewing large documents.
  • Programmatic navigation – Attach a ref to the Pdf component and call setPage(pageNumber) to jump to specific pages.
  • Custom loading UI – Pass a React element to the activityIndicator prop to replace the default spinner.

Example of programmatic page control:

const pdfRef = useRef(null);

const goToPage = (pageNum) => {
  pdfRef.current?.setPage(pageNum);
};

<Pdf
  ref={pdfRef}
  source={source}
  // ... other props
/>

Native Architecture and Platform Implementations

Understanding the underlying native code helps debug rendering issues. According to the react-native-pdf source:

  • Android – The view logic resides in android/src/main/java/com/wonday/reactnativepdf/ReactNativePdfView.java, which utilizes the Android PdfRenderer API introduced in API level 21 (Android 5.0). This class handles bitmap generation for pages and manages the file descriptor lifecycle.
  • iOS – The implementation in ios/RCTPdfView.m leverages Apple’s PDFKit framework, specifically PDFView and PDFDocument, to render content and handle zoom gestures natively.

Both implementations expose a unified JavaScript interface while managing platform-specific memory optimizations internally.

Android Permissions and Security

When loading PDFs from external storage on Android, declare the required permission in android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

For Android 6.0 (API 23) and above, request this permission at runtime using a library like react-native-permissions before attempting to load local files. iOS typically does not require additional permissions for sandboxed app directories, but ensure the file path is correctly formatted without the file:// scheme prefix for PDFKit compatibility.

Common Pitfalls and Troubleshooting

  • Missing native module – After installation, always rebuild the native app (npx react-native run-android or run-ios). The Pdf component requires the bridged native code compiled in ReactNativePdfView.java or RCTPdfView.m.
  • Incorrect URI schemes – Android requires the file:// prefix for absolute paths, whereas iOS accepts the raw absolute path. Using the wrong format results in a "file not found" error in the onError callback.
  • Memory exhaustion – Extremely large PDFs can trigger out-of-memory errors on older devices. Implement pagination or use the scale prop to reduce initial render resolution.

Summary

  • React Native lacks built-in PDF support, necessitating the react-native-pdf library for native rendering.
  • The Pdf component accepts source objects with remote URLs, local file paths, or base64 data, handling PDFKit and PdfRenderer complexity internally.
  • Platform-specific file path formatting and Android storage permissions are critical for local file display.
  • Advanced features like horizontal paging, programmatic navigation via refs, and custom loading indicators provide a polished user experience.
  • The native implementations in ReactNativePdfView.java (Android) and RCTPdfView.m (iOS) manage memory and gestures, exposing a declarative JavaScript API.

Frequently Asked Questions

Does react-native-pdf work with Expo?

No, react-native-pdf requires native modules (PDFKit on iOS and PdfRenderer on Android) that must be linked at the native level, which Expo’s managed workflow does not support without ejecting to a bare workflow or using a custom development client.

How do I display a PDF from a base64 string?

Construct a data URI by prefixing your base64 string with data:application/pdf;base64,, then pass it as the uri in the source object: source={{ uri: 'data:application/pdf;base64,' + base64String }}. The library decodes this internally in the native modules for both platforms.

Why does my PDF not appear on Android?

Verify that you have requested READ_EXTERNAL_STORAGE runtime permissions for external files and that local file URIs use the file:// scheme. Additionally, ensure you have run cd android && ./gradlew clean and rebuilt the app after installation, as the native Java code in ReactNativePdfView.java must be compiled into the APK.

Can I customize the loading spinner while the PDF loads?

Yes, pass a React element to the activityIndicator prop to replace the default native spinner. This renders while the library initializes the PDFDocument (iOS) or PdfRenderer (Android) in the background.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s https://instagit.com/install.md

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client