React File Upload - Complete Implementation Guide
A comprehensive guide to implementing file upload functionality in React applications with validation, preview, and proper error handling.
File Upload Component with Validation​
import React, { useState } from 'react';
const FileUploadForm = () => {
const [selectedImage, setSelectedImage] = useState(null);
const [selectedPdf, setSelectedPdf] = useState(null);
const [imagePreview, setImagePreview] = useState(null);
const [pdfPreview, setPdfPreview] = useState(null);
const [error, setError] = useState({ image: '', pdf: '' });
// Constants for file validation
const MAX_IMAGE_SIZE = 1 * 1024 * 1024; // 1MB in bytes
const MAX_PDF_SIZE = 500 * 1024; // 500KB in bytes
const ALLOWED_IMAGE_TYPES = ['image/jpeg', 'image/png'];
const ALLOWED_PDF_TYPE = 'application/pdf';
// Validate image file
const validateImage = (file) => {
// Reset error
setError(prev => ({ ...prev, image: '' }));
// Check file type
if (!ALLOWED_IMAGE_TYPES.includes(file.type)) {
setError(prev => ({
...prev,
image: 'Only JPEG and PNG files are allowed'
}));
return false;
}
// Check file size
if (file.size > MAX_IMAGE_SIZE) {
setError(prev => ({
...prev,
image: 'Image size should be less than 1MB'
}));
return false;
}
return true;
};
// Validate PDF file
const validatePdf = (file) => {
// Reset error
setError(prev => ({ ...prev, pdf: '' }));
// Check file type
if (file.type !== ALLOWED_PDF_TYPE) {
setError(prev => ({
...prev,
pdf: 'Only PDF files are allowed'
}));
return false;
}
// Check file size
if (file.size > MAX_PDF_SIZE) {
setError(prev => ({
...prev,
pdf: 'PDF size should be less than 500KB'
}));
return false;
}
return true;
};
// Format file size to readable format
const formatFileSize = (bytes) => {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
// Handle image upload
const handleImageChange = (event) => {
const file = event.target.files[0];
if (file) {
if (validateImage(file)) {
setSelectedImage(file);
// Create preview URL for image
const reader = new FileReader();
reader.onloadend = () => {
setImagePreview(reader.result);
};
reader.readAsDataURL(file);
}
}
// Reset input value if validation fails
if (error.image) {
event.target.value = null;
}
};
// Handle PDF upload
const handlePdfChange = (event) => {
const file = event.target.files[0];
if (file) {
if (validatePdf(file)) {
setSelectedPdf(file);
// Create preview URL for PDF
const url = URL.createObjectURL(file);
setPdfPreview(url);
}
}
// Reset input value if validation fails
if (error.pdf) {
event.target.value = null;
}
};
// Handle form submission
const handleSubmit = async (event) => {
event.preventDefault();
// Final validation check before submission
if (error.image || error.pdf) {
alert('Please fix the errors before submitting');
return;
}
const formData = new FormData();
if (selectedImage) formData.append('image', selectedImage);
if (selectedPdf) formData.append('pdf', selectedPdf);
try {
const response = await fetch('your-upload-api-endpoint', {
method: 'POST',
body: formData,
});
if (response.ok) {
alert('Files uploaded successfully!');
// Reset form
setSelectedImage(null);
setSelectedPdf(null);
setImagePreview(null);
setPdfPreview(null);
setError({ image: '', pdf: '' });
} else {
throw new Error('Upload failed');
}
} catch (error) {
console.error('Error uploading files:', error);
alert('Failed to upload files');
}
};
// Cleanup preview URLs
React.useEffect(() => {
return () => {
if (pdfPreview) {
URL.revokeObjectURL(pdfPreview);
}
};
}, []);
return (
<div className="max-w-2xl mx-auto p-6">
<form onSubmit={handleSubmit} className="space-y-6">
{/* Image Upload Section */}
<div className="space-y-2">
<label className="block text-sm font-medium text-gray-700">
Upload Image (PNG/JPEG, max 1MB)
</label>
<input
type="file"
accept=".png,.jpg,.jpeg"
onChange={handleImageChange}
className="block w-full text-sm text-gray-500
file:mr-4 file:py-2 file:px-4
file:rounded-md file:border-0
file:text-sm file:font-semibold
file:bg-blue-50 file:text-blue-700
hover:file:bg-blue-100"
/>
{error.image && (
<p className="text-red-500 text-sm mt-1">{error.image}</p>
)}
{selectedImage && (
<p className="text-sm text-gray-500">
Selected file size: {formatFileSize(selectedImage.size)}
</p>
)}
{imagePreview && (
<div className="mt-2">
<img
src={imagePreview}
alt="Preview"
className="max-w-xs h-auto rounded-lg shadow-md"
/>
</div>
)}
</div>
{/* PDF Upload Section */}
<div className="space-y-2">
<label className="block text-sm font-medium text-gray-700">
Upload PDF (max 500KB)
</label>
<input
type="file"
accept=".pdf"
onChange={handlePdfChange}
className="block w-full text-sm text-gray-500
file:mr-4 file:py-2 file:px-4
file:rounded-md file:border-0
file:text-sm file:font-semibold
file:bg-green-50 file:text-green-700
hover:file:bg-green-100"
/>
{error.pdf && (
<p className="text-red-500 text-sm mt-1">{error.pdf}</p>
)}
{selectedPdf && (
<p className="text-sm text-gray-500">
Selected file size: {formatFileSize(selectedPdf.size)}
</p>
)}
{pdfPreview && (
<div className="mt-2">
<iframe
src={pdfPreview}
className="w-full h-64 border rounded-lg"
title="PDF Preview"
/>
</div>
)}
</div>
{/* Submit Button */}
<button
type="submit"
className="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 transition-colors"
>
Upload Files
</button>
</form>
</div>
);
};
export default FileUploadForm;
Key Features​
- File Validation: Type and size validation for both images and PDFs
- Preview Generation: Image and PDF preview functionality
- Error Handling: Comprehensive error messages and validation
- FormData Usage: Proper FormData handling for file uploads
- Cleanup: Memory cleanup for preview URLs
- Responsive Design: Tailwind CSS styling for modern UI
Usage​
- Import the component into your React application
- Configure the upload endpoint in the
handleSubmitfunction - Customize validation rules as needed
- Style the component according to your design system