Skip to main content

JSI API

The PDFJSI module provides low-level JavaScript Interface (JSI) methods for direct native communication with zero bridge overhead. This enables up to 80x faster PDF operations compared to traditional React Native bridge.

Import

import PDFJSI from 'react-native-pdf-jsi/src/PDFJSI';

Core Methods

checkJSIAvailability()

Check if JSI is available on the current platform.

const isAvailable = await PDFJSI.checkJSIAvailability();
console.log('JSI Available:', isAvailable);

Returns: Promise<boolean>

  • true on Android (full JSI support)
  • true on iOS (enhanced bridge mode)
  • false on unsupported platforms

Platform Support:

  • Android: Full JSI acceleration
  • iOS: Enhanced bridge with optimizations
  • Windows: Not supported

renderPageDirect(pdfId, pageNumber, scale, base64Data)

Render a PDF page directly using JSI acceleration.

const result = await PDFJSI.renderPageDirect(
'my-pdf-123',
0, // Page number (0-indexed)
2.0, // Scale factor
base64PdfData // Base64 encoded PDF
);

console.log('Render success:', result.success);
console.log('Render time:', result.renderTime, 'ms');

Parameters:

  • pdfId (string): Unique PDF identifier
  • pageNumber (number): Page to render (0-indexed)
  • scale (number): Scale factor (1.0 = 100%, 2.0 = 200%)
  • base64Data (string): Base64 encoded PDF data

Returns: Promise<object>

{
success: boolean;
renderTime: number;
imageData?: string; // Base64 image data
}

Performance: ~2ms on average (vs 45ms with bridge)


getPageMetrics(pdfId, pageNumber)

Get detailed metrics for a specific page.

const metrics = await PDFJSI.getPageMetrics('my-pdf-123', 0);

console.log('Page size:', metrics.width, 'x', metrics.height);
console.log('Aspect ratio:', metrics.aspectRatio);

Returns: Promise<object>

{
width: number; // Page width in points
height: number; // Page height in points
aspectRatio: number; // Width / height ratio
renderTime?: number; // Last render time
cacheHit?: boolean; // Whether page was cached
}

preloadPagesDirect(pdfId, startPage, endPage)

Preload a range of pages into cache for faster access.

// Preload pages 10-20
await PDFJSI.preloadPagesDirect('my-pdf-123', 10, 20);
console.log('Pages 10-20 preloaded');

Parameters:

  • pdfId (string): PDF identifier
  • startPage (number): First page to preload
  • endPage (number): Last page to preload

Returns: Promise<boolean> - Success status

Use Case: Preload upcoming pages for smooth scrolling


searchTextDirect(pdfId, searchTerm, startPage, endPage)

Search for text within PDF pages using JSI.

const results = await PDFJSI.searchTextDirect(
'my-pdf-123',
'React Native',
0, // Start page
50 // End page
);

console.log(`Found ${results.length} matches`);
results.forEach(result => {
console.log(`Page ${result.page}: "${result.context}"`);
});

Returns: Promise<Array>

Array<{
page: number;
position: number;
context: string;
bounds?: { x: number; y: number; width: number; height: number };
}>

Performance: ~15ms per page (vs 120ms with bridge)


getCacheMetrics(pdfId)

Get cache statistics for a PDF.

const cacheMetrics = await PDFJSI.getCacheMetrics('my-pdf-123');

console.log('Cached pages:', cacheMetrics.cachedPages);
console.log('Cache size:', cacheMetrics.cacheSize, 'bytes');
console.log('Hit rate:', cacheMetrics.hitRate, '%');

Returns: Promise<object>

{
cachedPages: number; // Number of pages in cache
cacheSize: number; // Total cache size in bytes
hitRate: number; // Cache hit percentage (0-100)
missRate: number; // Cache miss percentage
totalRequests: number; // Total cache requests
}

clearCacheDirect(pdfId, cacheType)

Clear cache for improved memory management.

// Clear all caches
await PDFJSI.clearCacheDirect('my-pdf-123', 'all');

// Clear only page cache
await PDFJSI.clearCacheDirect('my-pdf-123', 'pages');

// Clear only image cache
await PDFJSI.clearCacheDirect('my-pdf-123', 'images');

Cache Types:

  • 'all' - Clear all caches (default)
  • 'pages' - Clear page cache only
  • 'images' - Clear rendered image cache
  • 'text' - Clear text extraction cache
  • 'base64' - Clear base64 data cache
  • 'bytes' - Clear byte array cache

optimizeMemory(pdfId)

Optimize memory usage for a PDF.

await PDFJSI.optimizeMemory('my-pdf-123');
console.log('Memory optimized');

What it does:

  • Releases unused cached pages
  • Compresses cached data
  • Cleans up temporary buffers
  • Triggers garbage collection hints

When to use:

  • After viewing many pages
  • Before loading new PDF
  • On memory warnings
  • Periodically for long sessions

setRenderQuality(pdfId, quality)

Set rendering quality for PDF pages.

// High quality (slower, better appearance)
await PDFJSI.setRenderQuality('my-pdf-123', 1.0);

// Medium quality (balanced)
await PDFJSI.setRenderQuality('my-pdf-123', 0.75);

// Low quality (faster, lower file size)
await PDFJSI.setRenderQuality('my-pdf-123', 0.5);

Quality Levels:

  • 1.0 - Best quality (default)
  • 0.75 - High quality
  • 0.5 - Medium quality
  • 0.25 - Low quality (draft mode)

getPerformanceMetrics(pdfId)

Get performance metrics for PDF operations.

const metrics = await PDFJSI.getPerformanceMetrics('my-pdf-123');

console.log('Total operations:', metrics.totalOperations);
console.log('Average duration:', metrics.averageDuration, 'ms');
console.log('Fastest operation:', metrics.fastest, 'ms');
console.log('Slowest operation:', metrics.slowest, 'ms');

Returns: Promise<object>

{
totalOperations: number;
averageDuration: number;
fastest: number;
slowest: number;
cacheHitRate: number;
jsiCallCount: number;
}

getJSIStats()

Get global JSI statistics (Android only).

const stats = await PDFJSI.getJSIStats();

console.log('JSI calls:', stats.totalJSICalls);
console.log('Bridge calls:', stats.totalBridgeCalls);
console.log('Performance gain:', stats.performanceGain, 'x');

Returns: Promise<object> (Android only)

{
totalJSICalls: number;
totalBridgeCalls: number;
averageJSITime: number;
averageBridgeTime: number;
performanceGain: number; // JSI speedup factor
}

Advanced Methods

lazyLoadPages(pdfId, currentPage, preloadRadius, totalPages)

Implement lazy loading with intelligent preloading.

await PDFJSI.lazyLoadPages(
'my-pdf-123',
10, // Current page
3, // Preload 3 pages before and after
100 // Total pages in PDF
);

What it does:

  • Loads current page immediately
  • Preloads pages within radius (10-3 to 10+3)
  • Unloads pages outside 2x radius
  • Optimizes memory automatically

progressiveLoadPages(pdfId, startPage, batchSize, onProgress)

Load pages progressively in batches with progress callbacks.

await PDFJSI.progressiveLoadPages(
'my-pdf-123',
1, // Start from page 1
5, // Load 5 pages per batch
(progress) => {
console.log(`Loading progress: ${progress}%`);
setLoadProgress(progress);
}
);

Use Case: Loading very large PDFs (100+ pages) without freezing UI


smartCacheFrequentPages(pdfId, frequentPages)

Intelligently cache frequently accessed pages.

// Cache table of contents and frequently visited pages
await PDFJSI.smartCacheFrequentPages('my-pdf-123', [1, 5, 10, 20, 50]);

What it does:

  • Prioritizes specified pages in cache
  • Prevents eviction of frequent pages
  • Optimizes cache hit rate

check16KBSupport()

Check if device supports 16KB page size (Android 15+ compliance).

const is16KBSupported = await PDFJSI.check16KBSupport();
console.log('16KB page size supported:', is16KBSupported);

Complete Example: High-Performance PDF Viewer

import React, { useState, useEffect } from 'react';
import { View, Text, Button, Alert } from 'react-native';
import PDFJSI from 'react-native-pdf-jsi/src/PDFJSI';
const PdfModule = require('react-native-pdf-jsi');
const Pdf = PdfModule.default;

const HighPerformanceViewer = ({ pdfPath }) => {
const [jsiAvailable, setJsiAvailable] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [totalPages, setTotalPages] = useState(0);
const [performanceMetrics, setPerformanceMetrics] = useState(null);
const pdfId = 'main-pdf';

// Initialize JSI
useEffect(() => {
const initJSI = async () => {
const available = await PDFJSI.checkJSIAvailability();
setJsiAvailable(available);

if (available) {
const stats = await PDFJSI.getJSIStats();
console.log('JSI Stats:', stats);
}
};
initJSI();
}, []);

// Lazy load pages as user navigates
useEffect(() => {
if (jsiAvailable && totalPages > 0) {
PDFJSI.lazyLoadPages(pdfId, currentPage, 3, totalPages)
.catch(err => console.error('Lazy load error:', err));
}
}, [currentPage, jsiAvailable, totalPages]);

// Update performance metrics periodically
useEffect(() => {
if (!jsiAvailable) return;

const interval = setInterval(async () => {
try {
const metrics = await PDFJSI.getPerformanceMetrics(pdfId);
setPerformanceMetrics(metrics);
} catch (error) {
console.error('Metrics error:', error);
}
}, 2000);

return () => clearInterval(interval);
}, [jsiAvailable]);

const handleLoadComplete = async (pages) => {
setTotalPages(pages);

// Preload first 10 pages
if (jsiAvailable) {
await PDFJSI.preloadPagesDirect(pdfId, 0, Math.min(9, pages - 1));
}
};

const handlePageChange = (page) => {
setCurrentPage(page);
};

const clearCache = async () => {
try {
await PDFJSI.clearCacheDirect(pdfId, 'all');
Alert.alert('Success', 'Cache cleared');
} catch (error) {
Alert.alert('Error', error.message);
}
};

const optimizeMemory = async () => {
try {
await PDFJSI.optimizeMemory(pdfId);
Alert.alert('Success', 'Memory optimized');
} catch (error) {
Alert.alert('Error', error.message);
}
};

return (
<View style={{ flex: 1 }}>
{/* Performance Badge */}
<View style={{
padding: 10,
backgroundColor: jsiAvailable ? '#4CAF50' : '#FF9800'
}}>
<Text style={{ color: 'white', fontWeight: 'bold' }}>
{jsiAvailable ? '⚡ JSI Accelerated' : '⚠️ Bridge Mode'}
</Text>
{performanceMetrics && (
<Text style={{ color: 'white', fontSize: 12 }}>
Avg: {performanceMetrics.averageDuration.toFixed(2)}ms |
Cache Hit: {performanceMetrics.cacheHitRate}%
</Text>
)}
</View>

{/* PDF Viewer */}
<Pdf
source={{ uri: pdfPath }}
onLoadComplete={handleLoadComplete}
onPageChanged={handlePageChange}
style={{ flex: 1 }}
/>

{/* Controls */}
<View style={{ flexDirection: 'row', padding: 10 }}>
<Button title="Clear Cache" onPress={clearCache} />
<View style={{ width: 10 }} />
<Button title="Optimize Memory" onPress={optimizeMemory} />
</View>
</View>
);
};

export default HighPerformanceViewer;

Performance Comparison

Real-world benchmarks comparing JSI vs Bridge:

OperationBridgeJSISpeedup
Page Render45ms2ms22.5x
Cache Access8ms0.1ms80x
Text Search120ms15ms8x
Page Metrics25ms1ms25x
Preload (10 pages)450ms20ms22.5x

Method Details

lazyLoadPages()

await PDFJSI.lazyLoadPages(pdfId, currentPage, preloadRadius, totalPages);

Efficiently load pages with intelligent caching:

  • Loads current page immediately
  • Preloads pages within radius (current ± radius)
  • Unloads pages beyond 2x radius
  • Optimizes memory automatically

Example:

// User is on page 50 of 200-page PDF
await PDFJSI.lazyLoadPages('pdf-1', 50, 3, 200);
// Loads: pages 47-53
// Keeps in cache: pages 44-56
// Unloads: pages < 44 and > 56

progressiveLoadPages()

await PDFJSI.progressiveLoadPages(pdfId, startPage, batchSize, onProgress);

Load pages in batches with progress feedback:

await PDFJSI.progressiveLoadPages(
'large-pdf',
1,
10, // 10 pages per batch
(progress) => {
console.log(`Progress: ${progress}%`);
setLoadingProgress(progress);
}
);

Use Case: Loading 100+ page PDFs without blocking UI


smartCacheFrequentPages()

await PDFJSI.smartCacheFrequentPages(pdfId, frequentPages);

Cache specific pages with high priority:

// Cache cover, TOC, and important chapters
await PDFJSI.smartCacheFrequentPages('textbook', [
1, // Cover
5, // Table of contents
20, // Chapter 1
45, // Chapter 2
80 // Chapter 3
]);

Benefits:

  • Instant access to important pages
  • Prevents eviction from cache
  • Optimizes overall cache hit rate

setRenderQuality()

await PDFJSI.setRenderQuality(pdfId, quality);

Control rendering quality for performance/quality tradeoff:

// Use case: Draft mode for previews
await PDFJSI.setRenderQuality('preview-pdf', 0.5);

// Use case: High-quality for final display
await PDFJSI.setRenderQuality('presentation-pdf', 1.0);

check16KBSupport()

const supported = await PDFJSI.check16KBSupport();

if (!supported) {
console.warn('Device may not support Google Play 16KB requirements');
}

Check Android 15+ compliance (Google Play requirement).


Error Handling

All methods may throw errors. Always use try-catch:

try {
await PDFJSI.renderPageDirect(pdfId, page, scale, data);
} catch (error) {
if (error.message.includes('JSI not available')) {
console.log('Falling back to bridge mode');
// Use standard Pdf component
} else if (error.message.includes('Platform')) {
console.log('Platform not supported');
} else {
console.error('Render error:', error);
}
}

Best Practices

1. Initialize Early

useEffect(() => {
PDFJSI.checkJSIAvailability().then(available => {
if (available) {
console.log('✅ JSI ready');
}
});
}, []);

2. Use Lazy Loading for Large PDFs

// For PDFs with 50+ pages
if (totalPages > 50) {
PDFJSI.lazyLoadPages(pdfId, currentPage, 3, totalPages);
} else {
// Preload all for smaller PDFs
PDFJSI.preloadPagesDirect(pdfId, 0, totalPages - 1);
}

3. Monitor Performance

useEffect(() => {
const checkPerf = async () => {
const metrics = await PDFJSI.getPerformanceMetrics(pdfId);
if (metrics.averageDuration > 50) {
console.warn('Performance degradation detected');
// Clear cache or optimize
await PDFJSI.optimizeMemory(pdfId);
}
};

const interval = setInterval(checkPerf, 30000);
return () => clearInterval(interval);
}, [pdfId]);

4. Clean Up on Unmount

useEffect(() => {
return () => {
PDFJSI.clearCacheDirect(pdfId, 'all')
.catch(err => console.error('Cleanup error:', err));
};
}, [pdfId]);

TypeScript

class PDFJSIManager {
checkJSIAvailability(): Promise<boolean>;
renderPageDirect(pdfId: string, pageNumber: number, scale: number, base64Data: string): Promise<RenderResult>;
getPageMetrics(pdfId: string, pageNumber: number): Promise<PageMetrics>;
preloadPagesDirect(pdfId: string, startPage: number, endPage: number): Promise<boolean>;
searchTextDirect(pdfId: string, searchTerm: string, startPage: number, endPage: number): Promise<SearchResult[]>;
getCacheMetrics(pdfId: string): Promise<CacheMetrics>;
clearCacheDirect(pdfId: string, cacheType?: CacheType): Promise<boolean>;
optimizeMemory(pdfId: string): Promise<boolean>;
setRenderQuality(pdfId: string, quality: number): Promise<void>;
getPerformanceMetrics(pdfId: string): Promise<PerformanceMetrics>;
getJSIStats(): Promise<JSIStats>;
lazyLoadPages(pdfId: string, currentPage: number, preloadRadius?: number, totalPages?: number | null): Promise<void>;
progressiveLoadPages(pdfId: string, startPage?: number, batchSize?: number, onProgress?: ((progress: number) => void) | null): Promise<void>;
smartCacheFrequentPages(pdfId: string, frequentPages?: number[]): Promise<void>;
check16KBSupport(): Promise<boolean>;
}

type CacheType = 'all' | 'pages' | 'images' | 'text' | 'base64' | 'bytes';

const PDFJSI: PDFJSIManager;
export default PDFJSI;

See Also