
Security News
TeamPCP and BreachForums Launch $1,000 Contest for Supply Chain Attacks
TeamPCP and BreachForums are promoting a Shai-Hulud supply chain attack contest with a $1,000 prize for the biggest package compromise.
@hubspire/react-native-chunk-upload
Advanced tools
A generalized React Native package for chunked file uploads with progress tracking
A generalized React Native package for file uploads with automatic chunking. This package automatically switches between chunked multipart uploads (for large files) and simple uploads (for smaller files) based on file size, supports concurrent uploads, and provides detailed progress callbacks.
expo-video-thumbnailsnpm install @hubspire/react-native-chunk-upload
# or
yarn add @hubspire/react-native-chunk-upload
# or
bun add @hubspire/react-native-chunk-upload
This package requires the following peer dependencies:
expo-file-system - For file system operationsnpm install expo-file-system
# or
yarn add expo-file-system
# or
bun add expo-file-system
For automatic video thumbnail generation (optional - only needed if you plan to upload videos):
npm install expo-video-thumbnails
# or
yarn add expo-video-thumbnails
# or
bun add expo-video-thumbnails
Note: The package will automatically generate thumbnails for videos if expo-video-thumbnails is installed. If not installed, video uploads will proceed without thumbnails.
The package provides a single unified uploadFiles function that automatically selects the best upload method based on file size. Files larger than the threshold use chunked multipart upload, while smaller files use simple upload.
import {
uploadFiles,
UnifiedUploadConfig,
FileUploadConfig,
} from "@hubspire/react-native-chunk-upload";
// Configure your upload
const uploadConfig: UnifiedUploadConfig = {
// File size threshold in bytes (default: 5MB)
// Files >= this size will use chunked upload, files < this size will use simple upload
chunkThresholdBytes: 5 * 1024 * 1024, // 5MB
// Required: Unified function to get signed URLs for chunked, simple, and thumbnail uploads
getUploadUrl: async ({
uploadType,
mediaType,
contentType,
extension,
totalParts,
}) => {
const response = await fetch("/api/upload/url", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
uploadType, // "chunked", "simple", or "thumbnail"
mediaType, // Not required for thumbnails
contentType,
extension,
totalParts, // Only used when uploadType is "chunked"
}),
});
if (!response.ok) throw new Error("Failed to get upload URL");
return response.json();
// Returns { urls, key, uploadId } for chunked or { url, key } for simple/thumbnail
},
// Required: Function to mark chunked upload as complete
markUploadComplete: async ({ eTags, key, uploadId }) => {
const response = await fetch("/api/upload/complete", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ eTags, key, uploadId }),
});
if (!response.ok) throw new Error("Failed to complete upload");
return response.json();
},
// Optional: Progress callback (includes both per-file and overall progress)
onProgress: (progress) => {
console.log(`File ${progress.fileIndex}: ${progress.percentComplete}%`);
console.log(`Overall: ${progress.overallPercentComplete}%`);
// progress includes: fileIndex, status, percentComplete, uploadedBytes, totalBytes, error
// Also includes: overallPercentComplete, totalUploadedBytes (overall progress across all files)
// status: "uploading" | "completed" | "failed"
// error: string | Error (only present if status === "failed")
},
// Optional: Configuration
chunkSize: 5 * 1024 * 1024, // 5MB chunks (default)
concurrentFileUploadLimit: 3, // Max 3 files at once (default)
concurrentChunkUploadLimit: 6, // Max 6 chunks at once (default)
maxFileSizeMB: 4096, // Max file size in MB (default: 4096MB = 4GB)
};
// Upload a single file (always pass as array)
const files: FileUploadConfig[] = [
{
fileIndex: 0,
filePath: "/path/to/file.jpg",
fileSize: 1024 * 1024 * 10, // 10MB - will use chunked upload (>= 5MB threshold)
mediaType: "photo",
contentType: "image/jpeg",
extension: "jpg",
},
];
const results = await uploadFiles(files, uploadConfig);
console.log("Upload result:", results[0]);
// Upload multiple files (mixed sizes)
const multipleFiles: FileUploadConfig[] = [
{
fileIndex: 0,
filePath: "/path/to/small-image.jpg",
fileSize: 1024 * 1024 * 2, // 2MB - will use simple upload (< 5MB threshold)
mediaType: "photo",
contentType: "image/jpeg",
extension: "jpg",
},
{
fileIndex: 1,
filePath: "/path/to/large-video.mp4",
fileSize: 1024 * 1024 * 50, // 50MB - will use chunked upload (>= 5MB threshold)
mediaType: "video",
// thumbnailPath is optional - will be auto-generated if expo-video-thumbnails is installed
contentType: "video/mp4",
extension: "mp4",
},
];
const uploadResults = await uploadFiles(multipleFiles, uploadConfig);
console.log("Upload results:", uploadResults);
The package automatically generates thumbnails for videos if expo-video-thumbnails is installed. You don't need to provide thumbnailPath - it will be generated automatically:
// Thumbnail will be auto-generated if expo-video-thumbnails is installed
const videoFile: FileUploadConfig = {
fileIndex: 0,
filePath: videoUri,
fileSize: videoSize,
mediaType: "video",
contentType: "video/mp4",
extension: "mp4",
// thumbnailPath is optional - will be auto-generated if expo-video-thumbnails is installed
};
const results = await uploadFiles([videoFile], uploadConfig);
Note: Thumbnails are automatically generated for videos if expo-video-thumbnails is installed. You don't need to manually generate or provide thumbnails - the package handles this internally.
uploadFiles(files, config)Uploads one or more files, automatically selecting chunked or simple upload based on file size.
Parameters:
files (required): Array of FileUploadConfig objects. Always pass an array, even for a single file.config (required): UnifiedUploadConfig object with all required callbacks and optional settings.Returns: Promise<UploadFileResult[]> - Array of upload results, one per file, in the same order as input.
Behavior:
fileSize >= chunkThresholdBytes use chunked multipart uploadfileSize < chunkThresholdBytes use simple uploadconcurrentFileUploadLimitUnifiedUploadConfigConfiguration object for unified uploads.
chunkThresholdBytes (optional): File size threshold in bytes. Files >= this size use chunked upload, files < this size use simple upload. Default: 5MB (5 _ 1024 _ 1024)getUploadUrl (required): Unified function to get signed URLs for chunked, simple, and thumbnail uploads. The library calls this with uploadType: "chunked", uploadType: "simple", or uploadType: "thumbnail" as needed.markUploadComplete (required): Function to complete chunked multipart uploadsonProgress (optional): Callback for per-file progress updates. Receives UploadProgress object that includes both per-file progress (fileIndex, status, percentComplete, etc.) and overall progress (overallPercentComplete, totalUploadedBytes) across all fileschunkSize (optional): Size of each chunk in bytes (default: 5MB)concurrentFileUploadLimit (optional): Max concurrent file uploads (default: 3)concurrentChunkUploadLimit (optional): Max concurrent chunk uploads (default: 6)maxFileSizeMB (optional): Maximum file size in MB (default: 4096)FileUploadConfigConfiguration for a single file upload.
fileIndex: Unique index identifier for this filefilePath: Local file path to uploadfileSize: File size in bytesmediaType: 'photo' or 'video'thumbnailPath (optional): Path to thumbnail image. If not provided for videos, will be auto-generated if expo-video-thumbnails is installedcontentType (optional): MIME content type (e.g., 'image/jpeg', 'video/mp4')extension (optional): File extension (e.g., 'jpg', 'mp4')UploadFileResultResult of a file upload.
fileIndex: File index that was uploadedmediaType: 'photo' or 'video'key: S3 key where the file is storedheight (optional): Image/video height in pixelswidth (optional): Image/video width in pixelsthumbnailKey (optional): S3 key of thumbnail (for videos)status (optional): Upload status ("completed" | "failed") - only present if upload finishederror (optional): Error message or Error object if upload failedUploadProgressProgress information for a file upload.
fileIndex: File index that this progress update is forstatus: Upload status ("uploading" | "completed" | "failed")totalParts (optional): Total number of chunks (for chunked uploads)uploadedParts (optional): Number of uploaded chunks (for chunked uploads)percentComplete (optional): Upload percentage (0-100)uploadedBytes (optional): Bytes uploaded so fartotalBytes (optional): Total file sizeerror (optional): Error message or Error object if upload failedoverallPercentComplete (optional): Overall progress percentage across all files (0-100)totalUploadedBytes (optional): Total bytes uploaded across all filesAn example React Native app demonstrating the package usage is available in the example/ directory. The example includes:
To run the example:
cd example
bun install # or npm install
npm start # or bun start
See the example README for detailed setup instructions.
Your backend needs to provide the following endpoints:
Required:
uploadType parameter ("chunked" or "simple") and returns appropriate response:
uploadType: "chunked": Returns { urls: string[], key: string, uploadId: string }uploadType: "simple": Returns { url: string, key: string }Optional:
See the example backend in example/backend/ for a complete implementation using AWS S3 and LocalStack.
The package works seamlessly with LocalStack for local development and testing. The example backend includes:
See example/backend/README.md for LocalStack setup instructions.
MIT
FAQs
A generalized React Native package for chunked file uploads with progress tracking
We found that @hubspire/react-native-chunk-upload demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 6 open source maintainers 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
TeamPCP and BreachForums are promoting a Shai-Hulud supply chain attack contest with a $1,000 prize for the biggest package compromise.

Security News
Packagist urges PHP projects to update Composer after a GitHub token format change exposed some GitHub Actions tokens in CI logs.

Research
GemStuffer abuses RubyGems as an exfiltration channel, packaging scraped UK council portal data into junk gems published from new accounts.