react-native-pdfrender
v0.1.6
Published
React Native PDF rendering library with TurboModules and Fabric components
Maintainers
Readme
react-native-pdfrender
A production-ready React Native PDF rendering library built on the New Architecture (TurboModules + Fabric components).
- Embedded viewer — drop
<PdfViewerView />anywhere in your render tree - Imperative viewer — use the
usePdfViewerhook to open a modal-style PDF viewer - Native rendering — CGPDFDocument / Core Graphics on iOS, Android PdfRenderer on Android
- Jetpack Compose — modern Android UI, no XML layouts
- Codegen-driven — TypeScript specs generate type-safe native bindings at build time
- Zero JS dependencies — no third-party npm packages required at runtime
Requirements
| Platform | Minimum version | |----------|----------------| | React Native | 0.70+ | | iOS | 13.0+ | | Android | SDK 24+ (Android 7.0) | | Node | 18+ |
Installation
npm install react-native-pdfrender
# or
yarn add react-native-pdfrenderiOS
cd ios && pod installAndroid
No extra steps — auto-linking handles it.
Usage
1. Declarative (embedded viewer)
import { PdfViewerView } from 'react-native-pdfrender';
function MyScreen() {
return (
<PdfViewerView
uri="file:///path/to/document.pdf"
initialPage={0}
defaultZoom={1.5}
isFullScreen={false}
style={{ flex: 1 }}
onZoomChange={(pct) => console.log('zoom:', pct)}
onFullScreenChange={(fs) => setFullScreen(fs)}
/>
);
}2. Imperative (hook — modal viewer)
import { usePdfViewer } from 'react-native-pdfrender';
function MyScreen() {
const { open, dismiss, getPageCount, createPdf } = usePdfViewer({
onFullScreenChanged: (isFS) => console.log('fullscreen:', isFS),
onZoomChanged: (zoom) => console.log('zoom:', zoom),
onDismiss: () => console.log('viewer closed'),
});
return (
<>
<Button title="Open PDF" onPress={() => open('file:///path/to/doc.pdf')} />
<Button title="Dismiss" onPress={dismiss} />
<Button title="Page count" onPress={async () => {
const n = await getPageCount('file:///path/to/doc.pdf');
console.log('pages:', n);
}} />
</>
);
}API
<PdfViewerView />
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| uri | string | required | file:// or content:// path to the PDF |
| initialPage | number | -1 (first) | 0-based page index to open on |
| defaultZoom | number | 1.5 | Initial zoom level (1.0 = 100%) |
| isFullScreen | boolean | false | Fills the parent container when true |
| rightLayout | boolean | false | Positions viewer on right side (split-view) |
| maxWidth | number | — | Max pixel width (split-view) |
| screenWidthPercentage | number | 100 | % of screen width |
| icons | IconSet | — | Custom toolbar icon assets |
| iconSize | number | 40 | Icon size in logical pixels |
| backButtonText | string | — | Toolbar back button label |
| headerText | string | — | Toolbar header label |
| style | ViewStyle | — | Container style |
| onFullScreenChange | (isFullScreen: boolean) => void | — | Fires when fullscreen state changes |
| onZoomChange | (zoomPercentage: number) => void | — | Fires when zoom changes |
| onLeftScreenChange | (isLeftScreen: boolean) => void | — | Fires in split-view |
| onRightScreenChange | (isRightScreen: boolean) => void | — | Fires in split-view |
IconSet
interface IconSet {
zoomIn?: number | string; // require('./zoom_in.png') or URI
zoomOut?: number | string;
fullScreen?: number | string;
minimizeScreen?: number | string;
leftLayout?: number | string;
rightLayout?: number | string;
}usePdfViewer(options?)
interface UsePdfViewerOptions {
onFullScreenChanged?: (isFullScreen: boolean) => void;
onZoomChanged?: (zoomPercentage: number) => void;
onDismiss?: () => void;
onError?: (error: string) => void;
}
interface UsePdfViewerReturn {
open: (uri: string, pageIndex?: number, defaultZoom?: number) => void;
dismiss: () => void;
getPageCount:(uri: string) => Promise<number>;
createPdf: (pages: PdfPage[], widthPx: number, heightPx: number) => Promise<PdfCreationResult>;
}NativePdfViewerModule (low-level TurboModule)
Exposed for advanced use cases. Prefer usePdfViewer for most scenarios.
import { NativePdfViewerModule } from 'react-native-pdfrender';
NativePdfViewerModule.openPdfViewer(uri, pageIndex, defaultZoom);
NativePdfViewerModule.dismissPdfViewer();
const count = await NativePdfViewerModule.getPdfPageCount(uri);
const result = await NativePdfViewerModule.createMultiPagePdfBase64(pages, width, height);Architecture
react-native-pdfrender/
├── android/
│ ├── build.gradle ← library module (com.android.library)
│ └── src/main/java/com/pdfrender/
│ ├── PdfViewerTurboModule.kt ← TurboModule (imperative API)
│ ├── PdfViewerFabricManager.kt ← Fabric ViewManager (embedded viewer)
│ ├── PdfViewerFragment.kt ← modal viewer Fragment
│ ├── PdfRenderingLogic.kt ← Android PdfRenderer wrapper
│ ├── ComposeRenderer.kt ← Jetpack Compose UI
│ └── events/ ← Fabric event classes
├── ios/
│ ├── PdfViewerTurboModule.mm ← ObjC++ TurboModule bridge
│ ├── PdfViewerTurboModuleImpl.swift ← Swift business logic
│ ├── PdfViewerComponentView.mm ← ObjC++ Fabric ViewManager
│ ├── PdfViewerView.swift ← main PDF rendering view
│ ├── PdfRenderingLogic.swift ← CGPDFDocument wrapper
│ └── ...
├── src/
│ ├── index.tsx ← public API exports
│ ├── PdfViewerView.tsx ← React component
│ ├── usePdfViewer.ts ← hook
│ └── specs/
│ ├── NativePdfViewerModule.ts ← TurboModule codegen spec
│ └── NativePdfViewerComponent.ts ← Fabric codegen spec
├── lib/ ← compiled output (generated by bob)
├── example/ ← example React Native app
└── react-native-pdfrender.podspecNew Architecture (codegen)
The codegenConfig in package.json points to src/specs/. At build time:
- iOS — CocoaPods generates
PdfViewerSpecs.hand the Fabric C++ headers - Android — Gradle generates the abstract
NativePdfViewerModuleSpecKotlin class
Building the library
# Install deps
yarn install
# Compile TypeScript → lib/
yarn build
# runs automatically as `prepare` before npm publish
# Type-check only
yarn typecheckRunning the example app
Install dependencies
# Library deps
yarn install
# Example deps
cd example && yarn installiOS (one-time Xcode project cleanup required)
The example Xcode project was migrated from the original monolithic app and still includes the library Swift files in the app target. Because CocoaPods now compiles those same files as part of the library pod, you must remove them from the app target first to avoid duplicate-symbol errors:
- Open
example/ios/pdfRender.xcodeprojin Xcode - Select the pdfRender app target → Build Phases → Compile Sources
- Remove these files (click each, press
–):PdfViewerView.swift,PdfViewerViewController.swiftPdfViewerTurboModuleImpl.swift,PdfRenderingLogic.swiftPdfPageView.swift,PdfToolbarView.swiftPdfCacheManager.swift,PdfConstants.swiftPdfViewerTurboModule.mm,PdfViewerComponentView.mm
- Save the Xcode project, then:
cd example/ios && pod install && cd ../..
yarn example:iosAndroid
yarn example:androidPublishing to npm
# Build the library
yarn prepare
# Verify what will be published
npm pack --dry-run
# Publish publicly
npm publish --access publicCommon Issues
PdfViewerSpecs/PdfViewerSpecs.h not found (iOS build)
Run pod install inside example/ios/ (or the consuming app's ios/). This triggers codegen which produces the PdfViewerSpecs headers.
Duplicate symbol errors on iOS
Follow the Xcode project cleanup steps above.
NativePdfViewerModuleSpec not found (Android)
Run the app once — Gradle auto-generates the spec. Or run manually:
cd example/android && ./gradlew generateCodegenArtifactsFromSchemaMetro can't resolve react-native-pdfrender
Run yarn install inside example/ (not just the root). The file:../ entry creates a symlink in example/node_modules/react-native-pdfrender that Metro follows.
License
MIT © Shubham Keshari
