Complete guide to file upload, storage with Vercel Blob, image processing, and file management in NextReady.
// components/file/FileUpload.tsx
'use client';
import { useState, useCallback } from 'react';
import { useDropzone } from 'react-dropzone';
import { Progress } from '@/components/ui/progress';
import { Button } from '@/components/ui/button';
import { Upload, X } from 'lucide-react';
interface FileUploadProps {
onUploadComplete?: (files: UploadedFile[]) => void;
maxFiles?: number;
maxSize?: number;
acceptedTypes?: string[];
}
export function FileUpload({
onUploadComplete,
maxFiles = 10,
maxSize = 10 * 1024 * 1024, // 10MB
acceptedTypes = ['image/*', '.pdf', '.doc', '.docx']
}: FileUploadProps) {
const [uploadProgress, setUploadProgress] = useState<Record<string, number>>({});
const [uploadedFiles, setUploadedFiles] = useState<UploadedFile[]>([]);
const onDrop = useCallback(async (acceptedFiles: File[]) => {
for (const file of acceptedFiles) {
await uploadFile(file);
}
}, []);
const uploadFile = async (file: File) => {
const formData = new FormData();
formData.append('file', file);
try {
const response = await fetch('/api/files/upload', {
method: 'POST',
body: formData,
});
if (!response.ok) throw new Error('Upload failed');
const result = await response.json();
setUploadedFiles(prev => [...prev, result.file]);
onUploadComplete?.([result.file]);
} catch (error) {
console.error('Upload error:', error);
}
};
const { getRootProps, getInputProps, isDragActive } = useDropzone({
onDrop,
maxFiles,
maxSize,
accept: acceptedTypes.reduce((acc, type) => {
acc[type] = [];
return acc;
}, {} as Record<string, string[]>),
});
return (
<div className="space-y-4">
<div
{...getRootProps()}
className={`border-2 border-dashed rounded-lg p-8 text-center cursor-pointer transition-colors ${
isDragActive ? 'border-brand-primary bg-brand-primary/5' : 'border-gray-300'
}`}
>
<input {...getInputProps()} />
<Upload className="mx-auto h-12 w-12 text-gray-400 mb-4" />
{isDragActive ? (
<p className="text-brand-primary">Drop files here...</p>
) : (
<div>
<p className="text-gray-600">Drag files here or click to browse</p>
<p className="text-sm text-gray-400 mt-2">
Max {maxFiles} files, up to {Math.round(maxSize / 1024 / 1024)}MB each
</p>
</div>
)}
</div>
{uploadedFiles.length > 0 && (
<div className="space-y-2">
<h4 className="font-medium">Uploaded Files</h4>
{uploadedFiles.map((file) => (
<div key={file.id} className="flex items-center justify-between p-2 border rounded">
<span className="text-sm">{file.name}</span>
<span className="text-xs text-gray-500">{formatFileSize(file.size)}</span>
</div>
))}
</div>
)}
</div>
);
}BLOB_READ_WRITE_TOKENRequiredVercel Blob storage token for read/write operations
vercel_blob_rw_AbCdEfGhIjKlMnOpQrStUvWxYzGo to your Vercel dashboard and enable Blob storage for your project
Generate a read-write token from the Blob storage settings
Add BLOB_READ_WRITE_TOKEN to your environment variables
Use the file upload components to test your configuration
Your file management system is configured and ready to handle uploads, storage, and processing.