![]() |
|
[Tut] React File Upload with Preview and Drag-and-Drop Support - Printable Version +- Sick Gaming (https://www.sickgaming.net) +-- Forum: Programming (https://www.sickgaming.net/forum-76.html) +--- Forum: PHP Development (https://www.sickgaming.net/forum-82.html) +--- Thread: [Tut] React File Upload with Preview and Drag-and-Drop Support (/thread-103627.html) |
[Tut] React File Upload with Preview and Drag-and-Drop Support - xSicKxBot - 12-10-2025 [Tut] React File Upload with Preview and Drag-and-Drop Support <div style="margin: 5px 5% 10px 5%;"><img src="https://www.sickgaming.net/blog/wp-content/uploads/2025/12/react-file-upload-with-preview-and-drag-and-drop-support.jpg" width="550" height="288" title="" alt="" /></div><div><div class="modified-on" readability="7.1489361702128"> by <a href="https://phppot.com/about/">Vincy</a>. Last modified on November 20th, 2025.</div> <p>This example contains a React drag-and-drop file upload with file type and size validation. It connects backend to upload files to the server via Axios.</p> <p>The workflow allows users to drop files to upload and shows file preview below the drop area. This file upload example includes the following featured functionalities.</p> <ol> <li><a href="https://phppot.com/react/react-drag-and-drop/">Drag and drop</a></li> <li>File validation</li> <li>Uploading to backend</li> <li>Saving file path to database</li> <li>Preview after upload</li> <li>Error handling</li> </ol> <p>It is easy to integrate into any React application, since it is structured with separate components for the upload and preview UI.</p> <p><img decoding="async" loading="lazy" class="alignnone size-large wp-image-24703" src="https://phppot.com/wp-content/uploads/2025/11/react-file-upload-preview-drag-drop-550x288.jpeg" alt="React File Upload Preview Drag Drop" width="550" height="288" srcset="https://phppot.com/wp-content/uploads/2025/11/react-file-upload-preview-drag-drop-550x288.jpeg 550w, https://phppot.com/wp-content/uploads/2025/11/react-file-upload-preview-drag-drop-300x157.jpeg 300w, https://phppot.com/wp-content/uploads/2025/11/react-file-upload-preview-drag-drop-768x402.jpeg 768w, https://phppot.com/wp-content/uploads/2025/11/react-file-upload-preview-drag-drop.jpeg 1200w" sizes="auto, (max-width: 550px) 100vw, 550px"></p> <h2>React UI with file upload interface</h2> <p>Two React components created for this file upload UI. Those are UploadBox and FilePreview.</p> <p>The UploadBox is the drop area for dragged files to be uploaded. Once upload completed, file thumbnails are shown in a preview box by using the FilePreview component.</p> <p>The FileUpload JSX handles the following processes before uploading a file.</p> <ol> <li><a href="https://phppot.com/jquery/file-size-validation-using-jquery/">File validation</a> about its extension and size.</li> <li>Handling errors or success acknowledgement for UI.</li> <li>Preparing form data with the file binaries.</li> </ol> <p><img decoding="async" loading="lazy" class="alignnone size-large wp-image-24564" src="https://phppot.com/wp-content/uploads/2025/11/react-file-upload-empty-state-550x155.jpg" alt="react file upload empty state" width="550" height="155" srcset="https://phppot.com/wp-content/uploads/2025/11/react-file-upload-empty-state-550x155.jpg 550w, https://phppot.com/wp-content/uploads/2025/11/react-file-upload-empty-state-300x84.jpg 300w, https://phppot.com/wp-content/uploads/2025/11/react-file-upload-empty-state-768x216.jpg 768w, https://phppot.com/wp-content/uploads/2025/11/react-file-upload-empty-state.jpg 1199w" sizes="auto, (max-width: 550px) 100vw, 550px"></p> <p class="code-heading"><code>src/components/FileUpload.jsx</code></p> <pre class="prettyprint"><code class="language-jsx">import { useState } from "react"; import axios from "axios"; import SERVER_SIDE_API_ROOT from "../../config"; import FilePreview from "./FilePreview"; import UploadBox from "./UploadBox"; import "../../public/assets/css/style.css"; const FileUpload = () => { const [files, setFiles] = useState([]); const [dragActive, setDragActive] = useState(false); const [uploading, setUploading] = useState(false); const [errorMsg, setErrorMsg] = useState(""); const allowedExtensions = ["jpg", "jpeg", "png", "gif", "pdf", "doc", "docx", "txt"]; const MAX_FILE_SIZE = 2 * 1024 * 1024; const uploadFiles = async (fileList) => { setErrorMsg(""); const safeFiles = []; const rejectedFiles = []; fileList.forEach((file) => { const ext = file.name.split(".").pop().toLowerCase(); if (!allowedExtensions.includes(ext)) return rejectedFiles.push("Invalid file type"); if (file.size > MAX_FILE_SIZE) return rejectedFiles.push("Maximum file size is 2MB."); if (file.size <= 0) return rejectedFiles.push("Empty file"); safeFiles.push(file); }); if (rejectedFiles.length > 0) { setErrorMsg(rejectedFiles[0]); setUploading(false); return; } if (!safeFiles.length) return; const formData = new FormData(); safeFiles.forEach((file) => formData.append("files[]", file)); setUploading(true); const delay = new Promise((resolve) => setTimeout(resolve, 800)); try { const res = await Promise.all([ axios.post(`${SERVER_SIDE_API_ROOT}/file-upload.php`, formData, { headers: { "Content-Type": "multipart/form-data" }, }), delay, ]); const uploadedFiles = res[0].data.files.filter((f) => f.status === "uploaded"); setFiles((prev) => [...prev, ...uploadedFiles.map(f => safeFiles.find(sf => sf.name === f.name))]); } catch { setErrorMsg("Server error — please try again later."); } setUploading(false); }; const handleDrop = async (e) => { e.preventDefault(); setDragActive(false); await uploadFiles(Array.from(e.dataTransfer.files)); }; return ( <div className="upload-wrapper"> <UploadBox dragActive={dragActive} uploading={uploading} errorMsg={errorMsg} handleDrop={handleDrop} setDragActive={setDragActive} > </UploadBox> <FilePreview files={files} uploading={uploading} /> </div> ); }; export default FileUpload; </code></pre> <h2>PHP file upload endpoint</h2> <p>The PHP script validates the received file binary before uploading to the server directory. If the validation passes, this script give name to the file with a <a href="https://phppot.com/php/php-random-string/">unique random id</a>.</p> <p>Once the PHP move_uploaded_file() saves the files to the directory, this code <a href="https://phppot.com/php/php-upload-image-to-database/">inserts the target path to the database</a>.</p> <p class="code-heading"><code>drag-drop-file-upload-api/file-upload.php</code></p> <pre class="prettyprint"><code class="language-php"><?php header("Access-Control-Allow-Origin: *"); header("Access-Control-Allow-Methods: POST, GET, OPTIONS"); header("Access-Control-Allow-Headers: Content-Type"); include "db.php"; $uploadDir = "uploads/"; $response = []; if (!file_exists($uploadDir)) { mkdir($uploadDir, 0777, true); } $allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'txt', 'doc', 'docx']; $maxFileSize = 2 * 1024 * 1024; foreach ($_FILES['files']['name'] as $key => $name) { $tmpName = $_FILES['files']['tmp_name'][$key]; $extension = strtolower(pathinfo($name, PATHINFO_EXTENSION)); $size = $_FILES['files']['size'][$key]; if (!in_array($extension, $allowedExtensions)) { $response[] = [ "name" => $name, "status" => "blocked", "message" => "File type not allowed (.{$extension})" ]; continue; } if ($size > $maxFileSize) { $response[] = [ "name" => $name, "status" => "blocked", "message" => "Maximum file size is 2M." ]; continue; } if ($size <= 0) { $response[] = [ "name" => $name, "status" => "blocked", "message" => "Empty file" ]; continue; } $uniqueName = uniqid() . "_" . basename($name); $targetPath = $uploadDir . $uniqueName; if (move_uploaded_file($tmpName, $targetPath)) { $stmt = $conn->prepare("INSERT INTO uploaded_files (file_name, file_path) VALUES (?, ?)"); $stmt->bind_param("ss", $uniqueName, $targetPath); $stmt->execute(); $response[] = [ "name" => $name, "path" => $targetPath, "status" => "uploaded" ]; } else { $response[] = [ "name" => $name, "status" => "failed", "message" => "Error moving uploaded file." ]; } } echo json_encode(["success" => true, "files" => $response]); ?> </code></pre> <h2>Drop area to place the dragged files</h2> <p>It contains UI elements to define the file drop area. The drop box uses onDragOver callback to highlight the drop area on hover.</p> <p>And, the onDrop callback prepares the form data to post the dropped file binary to the server.</p> <p><img decoding="async" loading="lazy" class="alignnone size-large wp-image-24578" src="https://phppot.com/wp-content/uploads/2025/11/react-file-upload-error-state-550x197.jpg" alt="react file upload error state" width="550" height="197" srcset="https://phppot.com/wp-content/uploads/2025/11/react-file-upload-error-state-550x197.jpg 550w, https://phppot.com/wp-content/uploads/2025/11/react-file-upload-error-state-300x107.jpg 300w, https://phppot.com/wp-content/uploads/2025/11/react-file-upload-error-state-768x275.jpg 768w, https://phppot.com/wp-content/uploads/2025/11/react-file-upload-error-state.jpg 1215w" sizes="auto, (max-width: 550px) 100vw, 550px"></p> <p class="code-heading"><code>src/components/UploadBox.jsx</code></p> <pre class="prettyprint"><code class="language-jsx">const UploadBox = ({ dragActive, uploading, errorMsg, handleDrop, setDragActive }) => ( <> <div className={`upload-box ${dragActive ? "active" : ""}`} onDragOver={(e) => { e.preventDefault(); setDragActive(true); }} onDragLeave={() => setDragActive(false)} onDrop={handleDrop} > <h3 className="upload-title">Drag & Drop Files Here</h3> <p className="upload-text">Files will upload automatically</p> </div> {uploading && <p className="uploading-text">Uploading...</p>} {errorMsg && <p className="error-text">{errorMsg}</p>} </> ); export default UploadBox; </code></pre> <h2>Showing file preview with thumbnails</h2> <p>The FilePreview component displays the uploaded files in a list format. It will show its thumbnail, name and size.</p> <p>If an image upload, the preview will show the image thumbnail. It a document type file is uploaded, the default icon is shown to the preview screen.<br /><img decoding="async" loading="lazy" class="alignnone size-large wp-image-24698" src="https://phppot.com/wp-content/uploads/2025/11/react-file-upload-success-case-output-550x421.jpg" alt="React File Upload Success Case Output" width="550" height="421" srcset="https://phppot.com/wp-content/uploads/2025/11/react-file-upload-success-case-output-550x421.jpg 550w, https://phppot.com/wp-content/uploads/2025/11/react-file-upload-success-case-output-300x230.jpg 300w, https://phppot.com/wp-content/uploads/2025/11/react-file-upload-success-case-output-768x588.jpg 768w, https://phppot.com/wp-content/uploads/2025/11/react-file-upload-success-case-output.jpg 1167w" sizes="auto, (max-width: 550px) 100vw, 550px"></p> <p class="code-heading"><code>src/components/FilePreview.jsx</code></p> <pre class="prettyprint"><code class="language-jsx">const FilePreview = ({ files, uploading }) => { if (!files.length) return null; return ( <div className="preview-list"> {files.map((file, i) => ( <div key={i} className="preview-row"> {file.type?.startsWith("image/") ? ( <div className="preview-thumb-wrapper"> <img src={URL.createObjectURL(file)} alt={file.name} className={`preview-thumb ${uploading ? "blurred" : ""}`} /> {uploading && ( <div className="preview-loader"> <img src="/assets/image/loader.svg" alt="Loading..." /> </div> )} </div> ) : ( <div className="file-icon"></div> )} <div className="file-info"> <p className="file-name">{file.name}</p> <p className="file-size">{Math.round(file.size / 1024)} KB</p> </div> </div> ))} </div> ); }; export default FilePreview; </code></pre> <h2>How to set up this application</h2> <p>The below steps help to set up this example to run in your environment. After these steps, start the npm dev server and run the React drag and drop app.</p> <ol> <li>Download the source and unzip into your computer.</li> <li>Copy the <code>drag-drop-file-upload-api</code> into the PHP web root.</li> <li>Create a database <code>file_upload_db</code> and import the SQL script in the <code>drag-drop-file-upload-api/sql</code></li> <li>Configure database details with <code>db.php</code></li> <li>Configure the PHP endpoint URL in React in <code>src/config.js</code></li> </ol> <h2>Conclusion</h2> <p>I hope the React code provides a modern file upload interface. The drag-and-drop, file validation, preview rendering and database insert is a stack of features enriches the example code. This code well-structured and ready to integrate with an application easily. If you want any add-on feature to this example, please let me know.</p> <p><strong>References:</strong></p> <ol> <li><a href="https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API" target="_blank" rel="noopener">HTML drag and drop UI</a></li> <li><a href="https://axios-http.com/docs/req_config" target="_blank" rel="noopener">Axios API request config option</a></li> </ol> <p><a class="download" href="https://phppot.com/downloads/react/react-file-upload-preview-drag-drop.zip">Download</a></p> <div class="written-by" readability="9.8427672955975"> <div class="author-photo"> <img loading="lazy" decoding="async" src="https://phppot.com/wp-content/themes/solandra/images/Vincy.jpg" alt="Vincy" width="100" height="100" title="Vincy"> </div> <div class="written-by-desc" readability="14.764150943396"> Written by <a href="https://phppot.com/about/">Vincy</a>, a web developer with 15+ years of experience and a Masters degree in Computer Science. She specializes in building modern, lightweight websites using PHP, JavaScript, React, and related technologies. Phppot helps you in mastering web development through over a decade of publishing quality tutorials. </div> </p></div> <p> <!-- #comments --> </p> <div class="related-articles"> <h2>Related Tutorials</h2> </p></div> <p> <a href="https://phppot.com/react/react-file-upload-preview-drag-drop/#top" class="top">↑ Back to Top</a> </p> </div> https://www.sickgaming.net/blog/2025/11/10/react-file-upload-with-preview-and-drag-and-drop-support/ |