Laravel
file upload
asynchronous
web development
PHP

Laravel uploading files asynchronously

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Asynchronous file upload in Laravel usually means the browser sends the file with JavaScript without forcing a full page reload, while Laravel handles validation and storage on the server. The upload itself is still an HTTP request, but from the user's point of view it feels non-blocking because the page remains interactive.

Server Side: Basic Upload Endpoint

Start with a normal Laravel controller method. The server-side logic is not special just because the request was initiated asynchronously.

php
1<?php
2
3namespace App\Http\Controllers;
4
5use Illuminate\Http\Request;
6use Illuminate\Support\Facades\Storage;
7
8class UploadController extends Controller
9{
10    public function store(Request $request)
11    {
12        $validated = $request->validate([
13            'file' => ['required', 'file', 'max:5120'],
14        ]);
15
16        $path = $validated['file']->store('uploads', 'public');
17
18        return response()->json([
19            'path' => $path,
20            'url' => Storage::disk('public')->url($path),
21        ]);
22    }
23}

Route example:

php
1use App\Http\Controllers\UploadController;
2use Illuminate\Support\Facades\Route;
3
4Route::post('/upload', [UploadController::class, 'store']);

This endpoint validates the file, stores it, and returns JSON that the frontend can consume immediately.

Frontend: Send the File with fetch

Use FormData so the browser sends a multipart request.

html
1<input type="file" id="fileInput">
2<button id="uploadBtn">Upload</button>
3
4<script>
5  const input = document.getElementById("fileInput");
6  const button = document.getElementById("uploadBtn");
7
8  button.addEventListener("click", async () => {
9    if (!input.files.length) return;
10
11    const formData = new FormData();
12    formData.append("file", input.files[0]);
13
14    const response = await fetch("/upload", {
15      method: "POST",
16      headers: {
17        "X-CSRF-TOKEN": document.querySelector('meta[name=\"csrf-token\"]').content
18      },
19      body: formData
20    });
21
22    const data = await response.json();
23    console.log(data);
24  });
25</script>

From the browser's perspective, this is asynchronous because the page keeps running while the request completes.

Validation and Error Handling Still Matter

Asynchronous uploads often fail because the success path was implemented first and the validation path was ignored. Since the frontend expects JSON, the error response should be predictable too.

On the client side:

javascript
1if (!response.ok) {
2  const errorData = await response.json();
3  console.error(errorData);
4  return;
5}

On the Laravel side, keep validation rules explicit so bad uploads fail early and consistently.

Add Progress Feedback

If you need upload progress, XMLHttpRequest is still convenient because fetch does not expose upload progress events in the same direct way.

javascript
1const xhr = new XMLHttpRequest();
2const formData = new FormData();
3formData.append("file", input.files[0]);
4
5xhr.upload.addEventListener("progress", (event) => {
6  if (event.lengthComputable) {
7    const percent = Math.round((event.loaded / event.total) * 100);
8    console.log(`Upload progress: ${percent}%`);
9  }
10});
11
12xhr.open("POST", "/upload");
13xhr.setRequestHeader("X-CSRF-TOKEN", document.querySelector('meta[name=\"csrf-token\"]').content);
14xhr.send(formData);

This is often enough for a progress bar without adding a JavaScript framework.

Queueing Is a Different Concern

Sometimes people say "async upload" when they really mean "upload first, process later." If the file requires expensive resizing, virus scanning, or media conversion, store it first and dispatch a queued job afterward. That keeps the HTTP request fast while the heavy work happens in the background.

That design is usually:

  • upload file
  • return success JSON
  • dispatch job for post-processing

This is a different layer of asynchrony than the browser request itself, but both are often useful in the same feature.

Common Pitfalls

The biggest mistake is forgetting CSRF handling when using JavaScript requests in a normal Laravel web app.

Another issue is assuming asynchronous upload changes the server-side validation rules. It does not. You still need normal Laravel file validation and storage logic.

A third problem is calling the feature "asynchronous" when the real need is background processing after upload. Upload transport and post-upload processing are separate concerns.

Summary

  • In Laravel, asynchronous upload usually means JavaScript submits the file without a full page reload.
  • The Laravel controller still validates and stores the file like any normal upload endpoint.
  • Use FormData on the frontend and return JSON from the server.
  • Use XMLHttpRequest if you need upload progress events.
  • If processing is expensive, upload first and queue the heavy work afterward.

Course illustration
Course illustration

All Rights Reserved.