Sick Gaming
[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 = () =&gt; { 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) =&gt; { setErrorMsg(""); const safeFiles = []; const rejectedFiles = []; fileList.forEach((file) =&gt; { const ext = file.name.split(".").pop().toLowerCase(); if (!allowedExtensions.includes(ext)) return rejectedFiles.push("Invalid file type"); if (file.size &gt; MAX_FILE_SIZE) return rejectedFiles.push("Maximum file size is 2MB."); if (file.size &lt;= 0) return rejectedFiles.push("Empty file"); safeFiles.push(file); }); if (rejectedFiles.length &gt; 0) { setErrorMsg(rejectedFiles[0]); setUploading(false); return; } if (!safeFiles.length) return; const formData = new FormData(); safeFiles.forEach((file) =&gt; formData.append("files[]", file)); setUploading(true); const delay = new Promise((resolve) =&gt; 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) =&gt; f.status === "uploaded"); setFiles((prev) =&gt; [...prev, ...uploadedFiles.map(f =&gt; safeFiles.find(sf =&gt; sf.name === f.name))]); } catch { setErrorMsg("Server error — please try again later."); } setUploading(false); }; const handleDrop = async (e) =&gt; { e.preventDefault(); setDragActive(false); await uploadFiles(Array.from(e.dataTransfer.files)); }; return ( &lt;div className="upload-wrapper"&gt; &lt;UploadBox dragActive={dragActive} uploading={uploading} errorMsg={errorMsg} handleDrop={handleDrop} setDragActive={setDragActive} &gt; &lt;/UploadBox&gt; &lt;FilePreview files={files} uploading={uploading} /&gt; &lt;/div&gt; );
};
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">&lt;?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 =&gt; $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" =&gt; $name, "status" =&gt; "blocked", "message" =&gt; "File type not allowed (.{$extension})" ]; continue; } if ($size &gt; $maxFileSize) { $response[] = [ "name" =&gt; $name, "status" =&gt; "blocked", "message" =&gt; "Maximum file size is 2M." ]; continue; } if ($size &lt;= 0) { $response[] = [ "name" =&gt; $name, "status" =&gt; "blocked", "message" =&gt; "Empty file" ]; continue; } $uniqueName = uniqid() . "_" . basename($name); $targetPath = $uploadDir . $uniqueName; if (move_uploaded_file($tmpName, $targetPath)) { $stmt = $conn-&gt;prepare("INSERT INTO uploaded_files (file_name, file_path) VALUES (?, ?)"); $stmt-&gt;bind_param("ss", $uniqueName, $targetPath); $stmt-&gt;execute(); $response[] = [ "name" =&gt; $name, "path" =&gt; $targetPath, "status" =&gt; "uploaded" ]; } else { $response[] = [ "name" =&gt; $name, "status" =&gt; "failed", "message" =&gt; "Error moving uploaded file." ]; }
}
echo json_encode(["success" =&gt; true, "files" =&gt; $response]);
?&gt;
</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 }) =&gt; ( &lt;&gt; &lt;div className={`upload-box ${dragActive ? "active" : ""}`} onDragOver={(e) =&gt; { e.preventDefault(); setDragActive(true); }} onDragLeave={() =&gt; setDragActive(false)} onDrop={handleDrop} &gt; &lt;h3 className="upload-title"&gt;Drag &amp; Drop Files Here&lt;/h3&gt; &lt;p className="upload-text"&gt;Files will upload automatically&lt;/p&gt; &lt;/div&gt; {uploading &amp;&amp; &lt;p className="uploading-text"&gt;Uploading...&lt;/p&gt;} {errorMsg &amp;&amp; &lt;p className="error-text"&gt;{errorMsg}&lt;/p&gt;} &lt;/&gt;
); 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 }) =&gt; { if (!files.length) return null; return ( &lt;div className="preview-list"&gt; {files.map((file, i) =&gt; ( &lt;div key={i} className="preview-row"&gt; {file.type?.startsWith("image/") ? ( &lt;div className="preview-thumb-wrapper"&gt; &lt;img src={URL.createObjectURL(file)} alt={file.name} className={`preview-thumb ${uploading ? "blurred" : ""}`} /&gt; {uploading &amp;&amp; ( &lt;div className="preview-loader"&gt; &lt;img src="/assets/image/loader.svg" alt="Loading..." /&gt; &lt;/div&gt; )} &lt;/div&gt; ) : ( &lt;div className="file-icon"&gt;&lt;/div&gt; )} &lt;div className="file-info"&gt; &lt;p className="file-name"&gt;{file.name}&lt;/p&gt; &lt;p className="file-size"&gt;{Math.round(file.size / 1024)} KB&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; ))} &lt;/div&gt; );
};
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/