High-performance React Native PDF viewer with JSI (JavaScript Interface) acceleration. A drop-in replacement for react-native-pdf with enhanced performance, Google Play compliance, and advanced features.
- Read PDFs from URL, blob, local file, or asset with caching support
- Horizontal and vertical display modes
- Pinch-to-zoom and drag with double-tap support (iOS & Android)
- Password-protected PDF support
- Programmatic page navigation
- Cross-platform support (iOS, Android, Windows)
- JSI Integration: Direct JavaScript-to-Native communication (up to 80x faster than bridge)
- Lazy Loading: Optimized loading for large PDF files with configurable preload radius
- Smart Caching: 30-day persistent cache with intelligent memory management
- Progressive Loading: Batch-based loading for optimal user experience
- Memory Optimization: Automatic memory management and cleanup for large documents
- Bookmarks: Create, edit, delete bookmarks with 10 custom colors and notes
- Reading Analytics: Track reading sessions, progress, speed, and engagement metrics
- Export Operations: Export pages to PNG/JPEG with quality control
- PDF Operations: Split, merge, extract, rotate, and delete pages
- PDF Compression: Reduce file sizes with 5 smart presets (EMAIL, WEB, MOBILE, PRINT, ARCHIVE)
- Text Extraction: Extract and search text with statistics and context
- File Management (Android): Download to public storage, open folders with MediaStore API
- Google Play 16KB Compliant: Built with NDK r28.2+ for Android 15+ requirements
- Future-Proof: Latest Android development toolchain and modern architecture
- Drop-in Replacement: Easy migration from existing PDF libraries
- Production Ready: Stable and tested in production environments
| Operation | Time | Throughput | Memory | vs Competition |
|---|---|---|---|---|
| 88MB PDF Compression | 13-16ms | 6,382 MB/s | 2 MB | 20-380x faster |
| Image Export (JPEG) | 37ms | N/A | 2 MB | 5.2x faster than PNG |
| Image Export (PNG) | 194ms | N/A | 2 MB | Baseline |
| File I/O Operations | <2ms | N/A | Minimal | Instant |
| Page Navigation | 0-3ms | N/A | Constant | Instant |
Key Achievements:
- O(1) Memory Complexity - Constant 2MB usage for files from 10MB to 10GB+
- 5.2x Faster Image Export - JPEG format with 90% quality (visually identical)
- 6+ GB/s Throughput - Industry-leading PDF compression speed
- Zero Crashes - Handles files other libraries can't (tested up to 10GB)
# Using npm
npm install react-native-pdf-jsi react-native-blob-util --save
# or using yarn
yarn add react-native-pdf-jsi react-native-blob-utilReact Native 0.60 and above:
cd ios && pod installReact Native 0.59 and below:
react-native link react-native-blob-util
react-native link react-native-pdf-jsiReact Native 0.59.0 and above:
Add the following to your android/app/build.gradle:
android {
packagingOptions {
pickFirst 'lib/x86/libc++_shared.so'
pickFirst 'lib/x86_64/libjsc.so'
pickFirst 'lib/arm64-v8a/libjsc.so'
pickFirst 'lib/arm64-v8a/libc++_shared.so'
pickFirst 'lib/x86_64/libc++_shared.so'
pickFirst 'lib/armeabi-v7a/libc++_shared.so'
}
}React Native 0.59.0 and below:
react-native link react-native-blob-util
react-native link react-native-pdf-jsi- Open your solution in Visual Studio 2019 (e.g.,
windows\yourapp.sln) - Right-click Solution icon in Solution Explorer > Add > Existing Project...
- Add
node_modules\react-native-pdf-jsi\windows\RCTPdf\RCTPdf.vcxproj - Add
node_modules\react-native-blob-util\windows\ReactNativeBlobUtil\ReactNativeBlobUtil.vcxproj - Right-click main application project > Add > Reference...
- Select
RCTPdfandReactNativeBlobUtilin Solution Projects - In app
pch.hadd:#include "winrt/RCTPdf.h" #include "winrt/ReactNativeBlobUtil.h"
- In
App.cppadd beforeInitializeComponent();:PackageProviders().Append(winrt::RCTPdf::ReactPackageProvider()); PackageProviders().Append(winrt::ReactNativeBlobUtil::ReactPackageProvider());
import React, { useState } from 'react';
import { View, StyleSheet } from 'react-native';
const PdfModule = require('react-native-pdf-jsi');
const Pdf = PdfModule.default;
export default function PDFExample() {
const [totalPages, setTotalPages] = useState(0);
const [currentPage, setCurrentPage] = useState(1);
const source = {
uri: 'https://example.com/document.pdf',
cache: true
};
return (
<View style={styles.container}>
<Pdf
source={source}
style={styles.pdf}
onLoadComplete={(numberOfPages, filePath, size) => {
console.log(`PDF loaded: ${numberOfPages} pages`);
setTotalPages(numberOfPages);
}}
onPageChanged={(page, numberOfPages) => {
console.log(`Current page: ${page} of ${numberOfPages}`);
setCurrentPage(page);
}}
onError={(error) => {
console.error('PDF Error:', error);
}}
trustAllCerts={false}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
pdf: {
flex: 1,
width: '100%',
height: '100%',
},
});Complete documentation is available at: https://euphonious-faun-24f4bc.netlify.app/
The documentation includes:
- API Reference
- Usage Guides
- Performance Optimization Tips
- Advanced Features Documentation
- Migration Guide from react-native-pdf
| Property | Type | Default | Description |
|---|---|---|---|
source |
object | required | PDF source like {uri: '...', cache: false} |
page |
number | 1 | Initial page index |
scale |
number | 1.0 | Scale factor (must be between minScale and maxScale) |
minScale |
number | 1.0 | Minimum scale |
maxScale |
number | 3.0 | Maximum scale |
horizontal |
boolean | false | Draw pages horizontally |
fitPolicy |
number | 2 | 0: fit width, 1: fit height, 2: fit both |
spacing |
number | 10 | Spacing between pages |
password |
string | "" | PDF password if required |
enablePaging |
boolean | false | Show only one page at a time |
enableRTL |
boolean | false | Right-to-left page order |
enableAntialiasing |
boolean | true | Enable antialiasing (Android only) |
enableAnnotationRendering |
boolean | true | Enable annotation rendering |
enableDoubleTapZoom |
boolean | true | Enable double tap to zoom |
singlePage |
boolean | false | Show only first page (thumbnail mode) |
trustAllCerts |
boolean | true | Allow self-signed certificates |
onLoadProgress |
function(percent) | null | Loading progress callback (0-1) |
onLoadComplete |
function(pages, path, size, tableContents) | null | Called when PDF loads |
onPageChanged |
function(page, numberOfPages) | null | Called when page changes |
onError |
function(error) | null | Called on error |
onPageSingleTap |
function(page) | null | Called on single tap |
onScaleChanged |
function(scale) | null | Called when scale changes |
onPressLink |
function(uri) | null | Called when link is tapped |
| Parameter | Description | Default |
|---|---|---|
uri |
PDF source (URL, file path, base64, etc.) | required |
cache |
Use cache or not | false |
cacheFileName |
Specific file name for cached PDF | SHA1(uri) |
expiration |
Cache expiration in seconds (0 = never) | 0 |
method |
HTTP method for URL sources | "GET" |
headers |
HTTP headers for URL sources | {} |
{uri: "http://xxx/xxx.pdf"}- Load from URL{uri: "file:///absolute/path/to/xxx.pdf"}- Load from local file{uri: "data:application/pdf;base64,JVBERi0xLjcKJc..."}- Load from base64{uri: "bundle-assets://xxx.pdf"}- Load from app bundle/assets{require("./test.pdf")}- Load from bundled asset (iOS only)
Programmatically navigate to a specific page.
const pdfRef = useRef(null);
<Pdf ref={pdfRef} source={source} />
// Navigate to page 42
pdfRef.current?.setPage(42);IMPORTANT: If you're using ProGuard or R8 code shrinking in your release builds, you must add the following rules to prevent crashes. These rules preserve JSI classes and native module interfaces that are required at runtime.
Add to your android/app/proguard-rules.pro file:
# react-native-pdf-jsi ProGuard Rules
# Keep all JSI-related classes
-keep class org.wonday.pdf.PDFJSIManager { *; }
-keep class org.wonday.pdf.PDFJSIModule { *; }
-keep class org.wonday.pdf.EnhancedPdfJSIBridge { *; }
-keep class org.wonday.pdf.RNPDFJSIPackage { *; }
# Keep PDF view classes
-keep class org.wonday.pdf.PdfView { *; }
-keep class org.wonday.pdf.PdfManager { *; }
-keep class org.wonday.pdf.RNPDFPackage { *; }
# Keep native methods (JNI)
-keepclasseswithmembernames class * {
native <methods>;
}
# Keep JSI interface methods
-keepclassmembers class * {
@com.facebook.react.bridge.ReactMethod *;
}
# Keep React Native bridge classes
-keep @com.facebook.react.bridge.ReactModule class * { *; }
-keep class com.facebook.react.bridge.** { *; }
# Keep Gson classes (used for serialization)
-keepattributes Signature
-keepattributes *Annotation*
-keep class com.google.gson.** { *; }
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
# Keep PdfiumAndroid classes
-keep class io.legere.pdfiumandroid.** { *; }
-keep class com.github.barteksc.pdfviewer.** { *; }
# Keep file downloader and manager classes
-keep class org.wonday.pdf.FileDownloader { *; }
-keep class org.wonday.pdf.FileManager { *; }
# Preserve line numbers for crash reporting
-keepattributes SourceFile,LineNumberTable
-renamesourcefileattribute SourceFile
Without these ProGuard rules, your release builds may experience:
- JSI initialization failures - Native methods won't be accessible
- PDF rendering crashes - Required classes may be obfuscated
- Event handler failures - React Native bridge methods may be removed
- Serialization errors - Gson classes needed for data conversion
After adding these rules, always test your release build:
# Build release APK
cd android && ./gradlew assembleRelease
# Test on device
adb install app/build/outputs/apk/release/app-release.apkIf you encounter crashes, check the stack trace and add additional -keep rules for any classes mentioned in the error logs.
Starting November 1, 2025, Google Play requires apps to support 16KB page sizes for Android 15+ devices. react-native-pdf-jsi is fully compliant with this requirement.
| Library | 16KB Support | Google Play Status | Migration Needed |
|---|---|---|---|
react-native-pdf |
Not Supported | Will be blocked | Required |
react-native-pdf-lib |
Unknown | Likely blocked | Required |
react-native-pdf-jsi |
Fully Supported | Compliant | None |
- NDK r28.2 - Latest Android development toolchain
- 16KB Page Size Support - Fully compliant with Google policy
- Android 15+ Ready - Future-proof architecture
- Google Play Approved - Meets all current and future requirements
- Base Memory: ~2MB for JSI runtime
- Per PDF: ~500KB average
- Cache Overhead: ~100KB per cached page
- Automatic Cleanup: Memory optimized every 30 seconds
- Zero Bridge Overhead: Direct memory access between JavaScript and native code
- Sub-millisecond Operations: Critical PDF operations execute in microseconds
- Enhanced Caching: Intelligent multi-level caching system
- Batch Operations: Process multiple operations efficiently
- Progressive Loading: Background preloading queue with smart scheduling
This package is a drop-in replacement for react-native-pdf. Simply change your import:
// Before
import Pdf from 'react-native-pdf';
// After
const PdfModule = require('react-native-pdf-jsi');
const Pdf = PdfModule.default;All existing props and callbacks work identically. No other code changes required.
Contributions are welcome! Please read our contributing guidelines before submitting pull requests.
- Clone the repository
- Install dependencies:
npm install - Build native libraries (Android):
cd android/src/main/cpp mkdir build && cd build cmake .. make
MIT License - see LICENSE file for details.
- Documentation: https://euphonious-faun-24f4bc.netlify.app/
- Issues: GitHub Issues
- Author: Punith M (@126punith)
Use v4.2.1 it contains stable fixes for IOS with unwanted debug logs removed
Fixed performance issue where path-related handlers were running unnecessarily when the path value hadn't actually changed. The fix filters out "path" from effectiveChangedProps when pathActuallyChanged=NO, preventing unnecessary reconfigurations of spacing, display direction, scroll views, usePageViewController, and other path-dependent handlers. This reduces unnecessary rerenders and improves performance, especially when navigating between pages. Addresses issue #7 (Page Prop Causes Full Rerender).
Fixed critical issue where pinch-to-zoom gestures were not working on iOS. The fix removes interfering custom gesture recognizers and enables PDFView's native pinch-to-zoom functionality, which now works smoothly on both iOS and Android.
See CHANGELOG.md for a complete list of changes and version history.