
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
react-native-unarchive
Advanced tools
An Archive Extraction Library for React Native Projects.
For React Native developers that need to extract RAR, ZIP, CBR and CBZ files in their apps, this package is a useful resource. It supports React Native's new Turbo Module architecture and was created in Kotlin and Objective-C++.
With this package, users can quickly and easily extract RAR, ZIP, CBR and CBZ archives and other compressed files using their device's native capabilities. Using this package, multiple archive formats can be processed, and it is simple to integrate into existing projects.
If you want to provide your React Native app the ability to read and extract RAR, ZIP, CBR, CBZ files, you should definitely give this package some thought.
npm install react-native-unarchive
# or
yarn add react-native-unarchive
Then run:
cd ios && pod install
import { unarchive, type UnarchiveResult } from 'react-native-unarchive';
import { DocumentDirectoryPath } from '@dr.pogodin/react-native-fs';
const extractArchive = async () => {
try {
const archivePath = '/path/to/your/archive.cbr'; // or .cbz
// IMPORTANT: outputPath must be within app sandbox (Documents/Caches/tmp)
const outputPath = `${DocumentDirectoryPath}/extracted`;
const result: UnarchiveResult = await unarchive(archivePath, outputPath);
console.log('Extraction completed!');
console.log('Output path:', result.outputPath);
console.log('Extracted files:', result.files);
// Access individual files
result.files.forEach((file) => {
console.log(`File: ${file.name}, Size: ${file.size}, Path: ${file.path}`);
console.log(` Relative path in archive: ${file.relativePath}`);
});
} catch (error) {
console.error('Extraction failed:', error);
}
};
import React, { useState } from 'react';
import { Alert } from 'react-native';
import { unarchive, type FileInfo } from 'react-native-unarchive';
import { pick } from '@react-native-documents/picker';
import { DocumentDirectoryPath, copyFile } from '@dr.pogodin/react-native-fs';
const ComicReader = () => {
const [extractedFiles, setExtractedFiles] = useState<FileInfo[]>([]);
const selectAndExtractArchive = async () => {
try {
// Pick archive file
const result = await pick({
type: ['application/zip', 'application/x-rar-compressed'],
});
if (result && result.length > 0) {
const selectedFile = result[0];
// For Android content URIs, copy to accessible location first
const archivePath = selectedFile.uri.startsWith('content://')
? `${DocumentDirectoryPath}/temp_archive${selectedFile.name}`
: selectedFile.uri;
if (selectedFile.uri.startsWith('content://')) {
await copyFile(selectedFile.uri, archivePath);
}
// Extract archive
const outputPath = `${DocumentDirectoryPath}/comics/${Date.now()}`;
const extractResult = await unarchive(archivePath, outputPath);
setExtractedFiles(extractResult.files);
Alert.alert('Success', `Extracted ${extractResult.files.length} files`);
}
} catch (error) {
Alert.alert('Error', `Failed to extract archive: ${error.message}`);
}
};
return (
// Your component JSX
);
};
import React from 'react';
import { Image, FlatList } from 'react-native';
import { type FileInfo } from 'react-native-unarchive';
interface ComicViewerProps {
files: FileInfo[];
}
const ComicViewer: React.FC<ComicViewerProps> = ({ files }) => {
// Filter for image files
const imageFiles = files.filter(file =>
/\.(jpg|jpeg|png|gif|bmp|webp)$/i.test(file.name)
);
const renderImage = ({ item }: { item: FileInfo }) => (
<Image
source={{ uri: `file://${item.path}` }}
style={{ width: 300, height: 400, margin: 10 }}
resizeMode="contain"
/>
);
return (
<FlatList
data={imageFiles}
renderItem={renderImage}
keyExtractor={(item) => item.path}
/>
);
};
unarchive(archivePath: string, outputPath: string): Promise<UnarchiveResult>Extracts the specified archive file to the given output directory.
Parameters:
archivePath (string): Full path to the archive file (CBR or CBZ)outputPath (string): Directory where files will be extractedReturns:
Promise<UnarchiveResult>: Promise that resolves with extraction resultsExample:
const result = await unarchive('/path/to/archive.cbr', '/path/to/output');
cancelUnarchive(): Promise<CancelResult>Cancels an ongoing extraction operation.
Returns:
Promise<CancelResult>: Promise that resolves when cancellation is completeExample:
import { Platform } from 'react-native';
import { unarchive, cancelUnarchive } from 'react-native-unarchive';
// Start extraction
const extractionPromise = unarchive(archivePath, outputPath);
try {
const result = await cancelUnarchive();
console.log('Cancelled:', result.cancelled);
} catch (error) {
console.error('Cancellation failed:', error);
}
// Handle extraction result or cancellation
try {
const result = await extractionPromise;
console.log('Extraction completed');
} catch (error) {
if (error.code === 'UNARCHIVE_CANCELLED') {
console.log('Extraction was cancelled by user');
}
}
FileInfoRepresents information about an extracted file.
interface FileInfo {
path: string; // Full path to the extracted file
name: string; // Filename (basename)
relativePath: string; // Relative path within the archive (preserves directory structure)
size: number; // File size in bytes
}
UnarchiveResultContains the results of an extraction operation.
interface UnarchiveResult {
files: FileInfo[]; // Array of extracted files
outputPath: string; // Path where files were extracted
}
CancelResultContains the result of a cancellation operation.
interface CancelResult {
cancelled: boolean; // Always true when cancellation succeeds
}
This library works well with popular React Native file system libraries:
import {
DocumentDirectoryPath,
CachesDirectoryPath,
TemporaryDirectoryPath,
exists,
readDir,
} from '@dr.pogodin/react-native-fs';
// IMPORTANT: Use app-scoped directories only
const outputDir = `${DocumentDirectoryPath}/comics`;
if (!(await exists(outputDir))) {
// Directory will be created automatically by unarchive
}
// List extracted files
const extractedFiles = await readDir(outputDir);
For security, the library enforces that extraction only occurs within app-scoped directories:
iOS Allowed Paths:
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, ...)NSSearchPathForDirectoriesInDomains(NSCachesDirectory, ...)NSTemporaryDirectory()In React Native with @dr.pogodin/react-native-fs:
import { DocumentDirectoryPath, CachesDirectoryPath, TemporaryDirectoryPath } from '@dr.pogodin/react-native-fs';
// ✅ Valid paths
const validPaths = [
`${DocumentDirectoryPath}/extracted`,
`${CachesDirectoryPath}/archives`,
`${TemporaryDirectoryPath}/temp-extract`,
];
// ❌ Invalid paths (will reject with UNARCHIVE_INVALID_PATH)
const invalidPaths = [
'/var/mobile/Containers/Shared', // Outside sandbox
'/Users/shared/archives', // Absolute path outside app
];
Android Allowed Paths:
context.filesDircontext.cacheDircontext.getExternalFilesDir(null)In React Native with @dr.pogodin/react-native-fs:
import { DocumentDirectoryPath, CachesDirectoryPath, ExternalStorageDirectoryPath } from '@dr.pogodin/react-native-fs';
// ✅ Valid paths
const validPaths = [
`${DocumentDirectoryPath}/extracted`, // App internal files
`${CachesDirectoryPath}/archives`, // App cache
`${ExternalStorageDirectoryPath}/Android/data/YOUR_PACKAGE/files`, // App external files
];
// ❌ Invalid paths (will reject with UNARCHIVE_INVALID_PATH)
const invalidPaths = [
'/sdcard/Download/archives', // Shared storage (Android 11+)
ExternalStorageDirectoryPath, // Root of external storage
];
The library provides detailed error information for common scenarios:
try {
const result = await unarchive(archivePath, outputPath);
} catch (error) {
if (error.code === 'UNARCHIVE_BUSY') {
console.log('Another extraction is in progress. Please wait and try again.');
} else if (error.code === 'UNARCHIVE_INVALID_PATH') {
console.log('Output path must be within app sandbox (Documents/Caches/tmp)');
} else if (error.code === 'FILE_NOT_FOUND') {
console.log('Archive file does not exist');
} else if (error.code === 'DIRECTORY_ERROR') {
console.log('Failed to create extraction directory');
} else if (error.code === 'UNSUPPORTED_FORMAT') {
console.log('Archive format not supported');
} else if (error.code === 'EXTRACTION_ERROR') {
console.log('Failed to extract archive contents');
// In debug builds, error may include partialFilesCount and partialFilesList
if (__DEV__ && error.userInfo) {
console.log('Partial files extracted:', error.userInfo.partialFilesCount);
console.log('Temp path:', error.userInfo.tempPath);
}
} else if (error.code === 'UNSAFE_PATH') {
console.log('Archive contains unsafe paths (potential ZIP-SLIP attack)');
} else if (error.code === 'UNARCHIVE_CANCELLED') {
console.log('Extraction was cancelled by user');
} else if (error.code === 'ATOMIC_REPLACE_ERROR') {
console.log('Failed to finalize extraction');
} else {
console.log('Unknown error:', error.message);
}
}
The library implements a busy-state check to prevent concurrent extractions:
UNARCHIVE_BUSY error immediatelyFor data safety and consistency:
The library includes protection against directory traversal attacks:
.. or absolute paths that attempt to escape the extraction directory are rejectedUNSAFE_PATH errorThe library preserves the original directory structure from archives:
relativePath field in FileInfo shows the file's path within the archivefolder1/image.jpg and folder2/image.jpg will extract both files to their respective directoriesIn debug builds, comprehensive logging is enabled:
// Debug logs appear in Metro/Xcode console
// Example output:
// [Unarchive] Starting CBR extraction: archive.cbr
// [Unarchive] Archive contains 125 entries
// [Unarchive] Extraction successful, enumerating files...
// [Unarchive] Enumerated 125 files
// [Unarchive] Extraction completed successfully with 125 files
Debug features include:
In release builds, logging is automatically disabled to reduce overhead.
Android: "File not found" with content URIs
// Copy content URI to accessible location first
if (uri.startsWith('content://')) {
const tempPath = `${DocumentDirectoryPath}/temp_${Date.now()}.cbr`;
await copyFile(uri, tempPath);
await unarchive(tempPath, outputPath);
}
iOS: CocoaPods dependency issues
cd ios
pod deintegrate
pod install
Images not displaying
// Ensure proper file:// URI format
const imageUri = file.path.startsWith('file://')
? file.path
: `file://${file.path}`;
<Image source={{ uri: imageUri }} />
Large archives causing memory issues
Enable debug logs to troubleshoot extraction issues:
// The library automatically logs extraction progress
// Check Metro/Xcode console for detailed information
| Archive Type | Format | Extension |
|---|---|---|
| Comic Book RAR | CBR | .cbr |
| Comic Book ZIP | CBZ | .cbz |
| General RAR | RAR | .rar |
| General ZIP | ZIP | .zip |
Contributions are welcome! Please read the contributing guidelines before submitting PRs.
MIT License - see LICENSE file for details.
For issues and questions:
FAQs
A React Native module to unarchive zip and rar files.
The npm package react-native-unarchive receives a total of 14 weekly downloads. As such, react-native-unarchive popularity was classified as not popular.
We found that react-native-unarchive demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.