A native, virtualized PDF viewer for React Native, built on Nitro Views and the New Architecture.
PDFKit on iOS, the platform PdfRenderer on Android: no bundled PDFium, no WebView, no JS in the scroll, zoom and render path.
Features • Installation • Quick start • Documentation • Capability matrix
- Performance first — scrolling, zooming, rendering and prefetching run natively. JS sends configuration and commands and receives settled events, never per-frame ones.
- The OS's own PDF engines — PDFKit on iOS,
PdfRendereron Android with a purpose-built native viewer. No bundled PDFium, no WebView, noreact-native-blob-util. - Memory bounded by the viewport — page virtualization, render cache, prefetch and cancellation on Android; pixels released under memory pressure and while a screen is hidden.
- Any source — local files, bundled assets (
require()),content://URIs and remote documents with headers, downloaded natively. - Several documents as one —
source={[a, b, c]}shows several files as one document, e.g. a setlist of songs: one page count, zoom, paging and search across all of them. - Offline reading — downloads kept for the session or across launches, revalidated with
ETag/Last-Modified;PdfCachedownloads ahead of time. - Layouts — vertical, horizontal, paged, single page and right to left; facing pages, translucent bars and dark pages.
- Reading position — settled viewport events, restore on open or on demand, end-of-document detection.
- Links, search and text selection — links followed or reported to JS, page taps in PDF points, native full-text search with highlights.
- Forms — AcroForm fields filled in by touch or from code, read back by name and exported as a filled-in PDF.
- Page overlays — React views attached to points of a page, moved natively while it scrolls and zooms.
- Document APIs — outline, page sizes, metadata, page images and printing, with or without a view.
- Password protection, typed errors, accessibility — stable error codes identical on both platforms, for load failures and method calls alike; VoiceOver and TalkBack read each page.
| Requirement | Version / note |
|---|---|
| React Native | 0.83+ |
| React Native architecture | New Architecture (Fabric); Nitro Views do not support the legacy renderer |
| Nitro Modules | react-native-nitro-modules >=0.37.0 |
| iOS | 15.1+ |
| Android | minSdkVersion 24+; some features need a newer Android, see the capability matrix |
| Expo | SDK 55+ with a development build (verified with SDK 57); Expo Go is not supported |
Developed and tested with React Native 0.86.3 / Expo SDK 57, Xcode 27, Kotlin 2.1, AGP 8.12 / Gradle 9.3, Node 24.
A document you have as a base64 string opens with source={{ base64 }}; see
Base64 documents. Known
gaps are listed in Limitations.
npm install react-native-better-pdf react-native-nitro-modulesYarn, pnpm and Bun work the same way. On bare React Native, run
cd ios && pod install and rebuild the app.
Expo: no config plugin is needed. Rebuild your development build:
npx expo prebuild
npx expo run:ios # or run:androidimport { PdfView } from 'react-native-better-pdf';
export function Reader() {
return (
<PdfView
style={{ flex: 1 }}
source={{ uri: 'https://example.com/report.pdf' }}
onLoad={({ pageCount }) => console.log('pages', pageCount)}
onPageChange={({ page, pageCount }) => console.log(page + 1, '/', pageCount)}
onError={(error) => console.warn(error.code, error.message)}
/>
);
}PdfView also takes every View prop. The API reference lists
all props and ref methods; the guides cover each feature.
| Guide | Covers |
|---|---|
| Sources and loading | Remote, local and bundled files, request headers, base64 data, several files as one document, loading state |
| Navigation and reading position | Links and page taps; saving and restoring where the reader left off |
| Offline reading and caching | Cache modes, revalidation, offline fallback, PdfCache |
| Layout and appearance | Facing pages, translucent bars, dark pages |
| Search | Native full-text search with highlights |
| Forms | Filling in, reading and exporting form fields |
| Page overlays | React views attached to points of a page |
| Page images and documents without a view | Page images, covers and metadata with PdfDocument |
| Printing | The system print dialog |
| Accessibility | VoiceOver and TalkBack |
- API reference — props, ref methods, objects, errors and types
- Limitations — known platform constraints and what's next
- Troubleshooting — common problems and their fixes
- Migrating from react-native-pdf — prop-by-prop migration guide
- Agent skill — teaches AI coding agents this API and the migration from react-native-pdf:
npx skills add gmi-software/better-skills --skill react-native-better-pdf - Releases — release notes for every version
- Architecture — threading, virtualization, cache, lifecycle
- API design — API decisions vs. react-native-pdf
- Benchmarks — methodology and results
- Platform research
- Audit — engineering audit and open issues
- Community requests — what react-native-pdf users ask for, and where this library stands
Several Android features come from the platform renderer's newer API: on
Android 15+, and on Android 12–14 when the device has SDK extension 13 (the
PDF module updated through Google Play system updates; the library then uses
PdfRendererPreV).
| Capability | iOS | Android 15+, or 12–14 with SDK extension 13 | Other Android versions |
|---|---|---|---|
| Viewing, zoom, layouts, offline, overlays, dark pages | Supported | Supported | Supported |
| Several sources as one document (reading only) | Supported | Supported | Supported |
Outline, page sizes, page images, printing, PdfDocument |
Supported | Supported (password-protected documents: no outline or metadata) | Supported |
| Links and page taps | Supported | Supported (password-protected documents: URL links and GoTo actions with an XYZ destination) | Supported |
| Password-protected documents | Supported | Supported | Unsupported: unsupported-pdf |
| Search | Supported | Supported | Unsupported: isSupported: false |
| Text selection | Supported | Supported | Unsupported: long presses are ignored |
| Forms: fill in, set from code, export | Supported | Supported | Read only: getFormFields works, setFormFieldValues rejects, exportDocument copies the file |
| Annotations drawn | All | Highlights, notes, free text and form fields; stamps from Android 17; no shapes, ink, underline or strike-out | Form fields |
| Screen readers read page text | Supported | Supported | The page label only |
Details are in each feature's guide and in Limitations.
Measured on release builds of the same example app, which contains both libraries; method and full results in docs/benchmarks.md.
Size (Android, one ABI, 2026-10-07):
| react-native-better-pdf | react-native-pdf 7.0.5 | |
|---|---|---|
| Native code and dex | ≈ 2.1 MB | ≈ 5.3 MB (bundled PDFium, AndroidPdfViewer, Gson, react-native-blob-util) |
Plus react-native-nitro-modules (≈ 1 MB), shared by every Nitro module in
an app.
Speed and memory (Android emulator, release builds, medians of 3 runs, 2026-10-18, both libraries in one session):
| Document | Load: better-pdf / rn-pdf | Load over HTTP, download included | Peak memory (PSS) | CPU while scrolling |
|---|---|---|---|---|
| 10 pages, text | 57 / 67 ms | 74 / 131 ms | 85 / 90 MB | 16 / 33% |
| 100 pages, images | 43 / 165 ms | 104 / 283 ms | 128 / 130 MB | 26 / 59% |
| 500 pages, scans | 47 / 417 ms | 592 / 1202 ms | 104 / 550 MB | 29 / 111% |
| 1000 pages, scans | 56 / 622 ms | — | 142 / 898 MB | 34 / 111% |
Pages that are all grey (text, greyscale scans) are kept in one byte per pixel: the same pixels in a quarter of the memory (results §20).
Page jumps reach the page-change event in 0 ms (react-native-pdf: 34–50 ms). On iOS both libraries render with PDFKit, so the numbers are close: better-pdf loads as fast or faster in most runs and peaks at the same or lower memory.
Coming from react-native-pdf? The migration guide maps every prop and shows how this library handles the issues its users report most.
- The main and JS threads never wait on document I/O or rendering.
- No per-frame JS: no
onScroll/onZoomevents, no React-managed pages. - Memory is bounded by the viewport, not the document or the zoom level.
- Claims come from measurements. docs/benchmarks.md describes the method and publishes the numbers — including where this library is not faster.
bun install
bun run fixtures # test documents (~650 MB), served by the next line
node benchmarks/serve-fixtures.mjs # on :8090 (Android: adb reverse tcp:8090 tcp:8090)
cd example && bunx expo run:ios # or run:androidThe example app lives in example, an Expo development build. It shows every feature on generated documents (forms, links, outlines, scans of 1,000 pages, encrypted and damaged files) and runs the Maestro end-to-end flows in example/e2e.
Contributions are welcome. CONTRIBUTING.md covers the toolchain, tests, end-to-end flows, benchmarks and commit conventions. Please report security issues privately as described in SECURITY.md.
MIT - see LICENSE.
