@kishannareshpal/expo-pdf
v0.3.1
Published
A cross-platform, performant PDF viewer component for React Native and Expo
Maintainers
Readme

@kishannareshpal/expo-pdf
A cross-platform, high-performance PDF viewer for React Native and Expo, built on top of native* PDF rendering engines.
| iOS | Android |
| -------------------------------------- | ---------------------------------------------- |
|
|
|
Features
- Supports Android and iOS
- Uses Apple's
PDFKiton iOS - *Uses kishannareshpal/AndroidPdfViewer on Android which is a maintained
fork of barteksc/AndroidPdfViewerV2 which uses the open-source PDFium PDF rendering engine.
- Note: We'll be looking to switch to
androidx.pdfon Android once that becomes stable.
- Note: We'll be looking to switch to
- Uses Apple's
- Load PDFs from local file paths
- Supports local file URIs on android and iOS and ContentResolver URIs on android
- Pinch-to-zoom / double-tap-to-zoom and drag gestures
- Support for password-protected PDFs
- Horizontal and vertical reading modes
- Support for content-insets
- Color inversion
Installation
This package works with both Expo and framework-less React Native projects but Expo provides a more streamlined experience.
Expo
npx expo install @kishannareshpal/expo-pdfBare React Native
Install this package
npm add @kishannareshpal/expo-pdf # bun add @kishannareshpal/expo-pdf # pnpm add @kishannareshpal/expo-pdf # yarn add @kishannareshpal/expo-pdfInstall CocoaPods dependencies
npx pod-install
Usage
Use a locally bundled PDF
import { PdfView } from '@kishannareshpal/expo-pdf';
export const App = () => {
const [assets] = useAssets([require('./assets/sample.pdf')]);
const [uri, setUri] = (useState < string) | (null > null);
useEffect(() => {
if (!assets?.length) {
return;
}
assets[0]
.downloadAsync()
.then((asset) => setUri(asset.localUri))
.catch(console.error);
}, [assets]);
if (!uri) {
return null;
}
return <PdfView style={{ flex: 1 }} uri={uri} />;
};Load a file picked using a system picker
import { File } from 'expo-file-system';
import * as DocumentPicker from 'expo-document-picker';
import { PdfView } from '@kishannareshpal/expo-pdf'
export const App = () => {
const [uri, setUri] = useState<string | null>(null)
const pickFile = () => {
try {
const result = await DocumentPicker.getDocumentAsync({ copyToCacheDirectory: true });
const file = new File(result.assets[0]);
setUri(file.localUri)
} catch (error) {
console.error(error);
}
}
if (!uri) {
return null;
}
return (
<View>
<Button onPress={pickFile}>Pick a file</Button>
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
{uri ? (
<PdfView
style={{ flex: 1 }}
uri={uri}
/>
) : (
<Text>Please pick a file<Text>
)}
</View>
</View>
)
}Use remote URLs
import { File } from 'expo-file-system';
import * as DocumentPicker from 'expo-document-picker';
import { PdfView } from '@kishannareshpal/expo-pdf'
export const App = () => {
const [uri, setUri] = useState<string | null>(null)
const loadFromUrl = (url: string) => {
const destination = new Directory(Paths.cache, 'pdfs');
try {
destination.create();
const output = await File.downloadFileAsync(url, destination);
setUri(output.uri)
} catch (error) {
console.error(error);
}
}
if (!uri) {
return null;
}
return (
<View>
<Button onPress={() => loadFromUrl("https://pdfobject.com/pdf/sample.pdf")}>
Download and load from URL
</Button>
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
{uri ? (
<PdfView
style={{ flex: 1 }}
uri={uri}
/>
) : (
<Text>Please download the file to preview<Text>
)}
</View>
</View>
)
}API
API Reference
ContentPadding
{ top?: number, left?: number, right?: number, bottom?: number }FitMode
'width' | 'height' | 'both';OnLoadCompleteEventPayload
{
pageCount: number;
}OnPageChangedEventPayload
{ pageIndex: number, pageCount: number }OnErrorEventPayload
{
code: 'invalid_url' | 'invalid_document' | 'password_required' | 'password_incorrect',
message: string
}Support for CSS using uniwind (TailwindCSS)
To use uniwind class names, you must wrap the component with the withUniwind Higher-Order Component (HOC).
This allows the component to process the className prop and convert it into native styles.
// src/lib/styled.tsx
import { PdfView as PdfViewPrimitive } from '@kishannareshpal/expo-pdf';
import { ComponentProps } from 'react';
import { withUniwind } from 'uniwind';
export const StyledPdfViewPrimitive = withUniwind(PdfViewPrimitive);
export type StyledPdfViewPrimitiveProps = ComponentProps<
typeof StyledPdfViewPrimitive
>;// src/app.tsx
import { StyledPdfView } from '@/lib/styled';
const App = () => {
return (
<StyledPdfView
className="flex-1 rounded-4xl bg-red-500"
uri="..."
...
/>
)
}References
Prior art
This project is inspired by react-native-pdf and maintained to the more modern React Native and Expo ecosystem.
Contributing
Contributions are welcome!
How to contribute
Please read CONTRIBUTING.md
How to publish a new version to the registry
NOTE
This package follows semantic versioning with the format:
major.minor.patch(-beta.n|next).
- Major version: Increment when making incompatible API changes.
- Minor version: Increment when adding new functionality in a backward-compatible way.
- Patch version: Increment when fixing bugs in a backward-compatible manner.
Only approved repository members with access to our NPM account can publish new versions.
- Ensure all changes ready for release are merged into
main. - Go to the
✳️ Release Packageworkflow - Click the
Run workflowdropdown button and configure the inputs:Dry run: Uncheck this to perform the actual release (Default istrueto test the build/versioning without publishing - only useful when updating therelease.ymlworkflow during maintenance)NPM tag: Leave aslatestfor standard releases, or usebeta/nextfor pre-releases.Version override: Leave asautoto let it automatically increment the patch version (or beta version ifbetawas specified above). if you to bump the minor or major version, you must specify that here (e.g.1.2.0or2.0.0)
- Click the
Run workflowbutton to publish 🚀- Monitor the status at kishannareshpal/expo-pdf/actions
License
MIT
