Skip to main content

PDFViewer

PDF document viewer component built on react-native-pdf with enhanced loading states, theme integration, and automatic error handling. Provides a seamless document viewing experience with smooth loading animations and consistent styling.

Props

NameTypeRequiredDefaultDescription
sourceURIstring-URI of the PDF document to display
containerStyleStyleProp<ViewStyle>--Custom styles for the container view
styleStyleProp<ViewStyle>--Custom styles for the PDF component
testIDstring-"pdf-viewer"Test identifier for the component
onLoadComplete(numberOfPages: number, path: string, size: {width: number, height: number}, tableContents?: TableContent[]) => void--Callback when PDF loading completes

Inherits all props from PdfProps except source

Additional Inherited Props

  • onError - Error handling callback
  • onPageChanged - Page change callback
  • onProgress - Loading progress callback
  • enablePaging - Enable page-based scrolling
  • horizontal - Horizontal scrolling mode
  • fitPolicy - PDF fit policy (default: 0)
  • spacing - Page spacing
  • password - PDF password if required

Component Behavior

Loading States

  1. Initial Loading: Shows animated loading indicator with theme-aware backdrop
  2. Document Ready: Displays PDF content with fade-in animation
  3. Error State: Handles loading failures through error callbacks

Animation System

  • Uses react-native-reanimated for smooth fade in/out transitions
  • Loading indicator appears immediately when sourceURI is provided
  • Content fades in seamlessly when document is ready

Theme Integration

  • Automatically applies theme colors for loading backdrop
  • Respects app theme for consistent visual experience
  • Uses useAppTheme hook for dynamic theming

Styling

Container Customization

const containerStyle = {
backgroundColor: '#f5f5f5',
borderRadius: 8,
margin: 16,
};

<PDFViewer
sourceURI="https://example.com/document.pdf"
containerStyle={containerStyle}
/>

PDF Content Styling

const pdfStyle = {
flex: 1,
backgroundColor: 'white',
};

<PDFViewer
sourceURI="https://example.com/document.pdf"
style={pdfStyle}
/>

Full Screen PDF

<PDFViewer
sourceURI="https://example.com/document.pdf"
containerStyle={{ flex: 1 }}
style={{ flex: 1 }}
/>

Usage Patterns

Privacy Policy Display

import * as React from "react";
import { PDFViewer } from "@ovok/native";

export const PrivacyPolicyScreen = () => {
return (
<PDFViewer
sourceURI="https://storage.example.com/privacy-policy.pdf"
testID="privacy-policy-viewer"
/>
);
};

Terms and Conditions

import * as React from "react";
import { PDFViewer } from "@ovok/native";

export const TermsScreen = () => {
const handleLoadComplete = (pages: number) => {
console.log(`Terms document has ${pages} pages`);
};

return (
<PDFViewer
sourceURI="https://storage.example.com/terms.pdf"
onLoadComplete={handleLoadComplete}
containerStyle={{ margin: 16 }}
/>
);
};

Device Instruction Manual

import * as React from "react";
import { Alert } from "react-native";
import { PDFViewer } from "@ovok/native";

interface DeviceManualProps {
deviceType: string;
manualUrl: string;
}

export const DeviceManual = ({ deviceType, manualUrl }: DeviceManualProps) => {
const handleError = (error: Error) => {
Alert.alert(
"Manual Not Available",
`Unable to load ${deviceType} instruction manual. Please try again later.`
);
};

return (
<PDFViewer
sourceURI={manualUrl}
onError={handleError}
testID={`manual-${deviceType.toLowerCase()}`}
enablePaging
horizontal={false}
/>
);
};

Healthcare Document Viewer

import * as React from "react";
import { View, StyleSheet } from "react-native";
import { ActivityIndicator, Text } from "react-native-paper";
import { PDFViewer } from "@ovok/native";

interface HealthcareDocProps {
documentId: string;
documentUrl: string;
title: string;
}

export const HealthcareDocumentViewer = ({
documentId,
documentUrl,
title
}: HealthcareDocProps) => {
const [isLoading, setLoading] = React.useState(true);
const [error, setError] = React.useState<string | null>(null);

const handleLoadComplete = () => {
setLoading(false);
setError(null);
};

const handleError = (err: Error) => {
setLoading(false);
setError(err.message);
};

if (error) {
return (
<View style={styles.errorContainer}>
<Text variant="bodyLarge">Unable to load document</Text>
<Text variant="bodyMedium">{error}</Text>
</View>
);
}

return (
<PDFViewer
sourceURI={documentUrl}
onLoadComplete={handleLoadComplete}
onError={handleError}
testID={`healthcare-doc-${documentId}`}
containerStyle={styles.container}
/>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
margin: 16,
},
errorContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 24,
},
});

Password Protected PDF

import * as React from "react";
import { PDFViewer } from "@ovok/native";

interface SecureDocumentProps {
documentUrl: string;
password: string;
}

export const SecureDocument = ({ documentUrl, password }: SecureDocumentProps) => {
return (
<PDFViewer
sourceURI={documentUrl}
password={password}
onError={(error) => {
console.error('PDF Error:', error);
}}
testID="secure-document"
/>
);
};

Error Handling

Network Errors

const handleNetworkError = (error: Error) => {
if (error.message.includes('network')) {
// Handle network connectivity issues
showNetworkErrorDialog();
} else {
// Handle other PDF errors
showGenericErrorDialog();
}
};

<PDFViewer
sourceURI={documentUrl}
onError={handleNetworkError}
/>

Validation and Fallbacks

const ValidatedPDFViewer = ({ url }: { url: string }) => {
if (!url) {
return <DocumentNotAvailable />;
}

return (
<PDFViewer
sourceURI={url}
onError={() => showDocumentErrorAlert()}
/>
);
};

Performance Considerations

Large Documents

  • Use appropriate fitPolicy settings for optimal rendering
  • Consider implementing pagination for very large documents
  • Monitor memory usage with large PDF files

Network Optimization

  • Implement proper loading states for slow connections
  • Consider caching strategies for frequently accessed documents
  • Handle offline scenarios gracefully

Animation Performance

  • Loading animations are optimized with react-native-reanimated
  • Smooth 60fps transitions without JavaScript bridge overhead
  • Minimal performance impact on document rendering