Passing uploaded files between Laravel Livewire components
Step-by-step guide to passing uploaded files between Laravel Livewire components using TemporaryUploadedFile, file paths, and reusable upload logic.

Search for a command to run...
Step-by-step guide to passing uploaded files between Laravel Livewire components using TemporaryUploadedFile, file paths, and reusable upload logic.

No comments yet. Be the first to comment.
Sharing our secrets among our teammates and across our environments was becoming a liability. So I decided to take matters into my own hands.

Let's be real with ourselves for a moment: we don't like to be onboarded. Whether you are the person who is responsible for onboarding new employees, or you yourself are the one being onboarded into a new role; it's a painful process.

Being developers and managers with boots on the ground, we owe it to our customers and users to be the voice of reason and stability when deploying AI-generated assets. Despite what is being asked of us from leadership.

Don't be that guy

When a requested update or bugfix comes across my desk, there is nothing more daunting when a fresh clone/fork of repository completes and then the installation process ensues. If you are like me, you

The Technical Leader
10 posts
Bringing 15+ years of software and development expertise married with decades of leadership experience, I provide a unique and honest take on leading software teams and cultivating your own growth. As I am still a developer at heart, I will occasionally talk about new trends and experiences that have forced me to take a side, whether it's right or wrong.
I recently discovered a need to be able pass temporary (without being saved to your filesystem) uploaded files using Laravel Livewire for an API that I am building for my 9-5 job. Essentially (thinking with my VueJS cap) I wanted to build a uniform file-upload input that I could use multiple times within my application.
Again, using my VueJS-centered brain, I just assumed that I would be able to simply emit up the TemporaryUploadedFile class that Livewire generates with a file-upload. Naturally I was incorrect. My problem: when you attempt to pass this class to a parent/child component all you get is:
Obviously you cannot do anything with this. So after some discovery, I was able to come up with a scalable solution.
For the sake of being thorough, I am going to show you all of my code, so you can replicate the entire component for yourself. It is worth noting that I also used Alpine.JS and Tailwind for this component.
In the next few steps, this is what we are going to build:
Yours MIGHT look a little different as I have modified my tailwind.config.js file a bit.
Assuming you have at least skimmed the documentation for Laravel Livewire on file uploads, you can see that once a file is uploaded, Livewire stores the file in a temporary directory within your filesystem; whether it is remote or locally depends on your infrastructure. The response of this action is an object with a bunch of other meta data named Livewire\TemporaryUploadedFile. From there you can extract any information that you need and save/move the file around where you need it. Again, you can refer to the documentation for further details.
Like I said above, if you simply try to use the emit() action to pass the result to other components, all that is passed is the disk path. I even tried JSON encoding the object, but that didn't work either. After dissecting the Livewire\TemporaryUploadedFile class, I discovered there is an exposed method getRealPath() that returns the temporary location of the file. Furthermore, I found that the path of the file is the parameter required to construct a new instance of the TemporaryUploadedFile class. Passing strings with Laravel Livewire is easy, so that was my ticket: emitting the full real path of the temporary file, and then simply re-constructing the TemporaryUploadedFile class in the parent/listening component.
Once finished, then you can either move/save the emitted file like so:
class FileUpload extends Component
{
use WithFileUploads;
public $file;
...
public function fileComplete()
{
$this-file->storePubliclyAs(
'documents',
'sample',
config('filesystems.default'),
);
}
}
<div
x-data="{ isUploading: false, progress: 0, name: null }"
x-on:livewire-upload-start="isUploading = true"
x-on:livewire-upload-finish="isUploading = false; $wire.fileComplete()"
x-on:livewire-upload-error="isUploading = false"
x-on:livewire-upload-progress="progress = $event.detail.progress"
>
<div class="overflow-hidden relative w-64 mt-4 mb-4">
<label class="font-sans py-2 px-4 border border-transparent text-sm font-semibold tracking-wider rounded-md transition duration-150 ease-in-out uppercase border-gray-300 text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue inline-flex cursor-pointer" x-show="!name">
<svg fill="#FFF" height="18" viewBox="0 0 24 24" width="18" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z"/>
</svg>
<span class="ml-2">Select File</span>
<input class="hidden" type="file" wire:model="file" x-on:change="name = $event.target.files[0].name">
</label>
<div class="text-gray-500" x-show="name">
<span x-show="isUploading">Uploading... Please wait.</span>
<div class="flex items-center" x-show="!isUploading">
<div class="flex-1 truncate mr-2">
<span x-text="name"></span>
</div>
<button type="button" class="inline-flex items-center justify-center h-7 w-7 rounded-full bg-red-600 hover:bg-red-800 text-white shadow-lg hover:shadow-xl transition duration-150 ease-in-out focus:bg-red-700 outline-none focus:outline-none" x-on:click="progress = 0; name = null; \(wire.file = null; \)wire.fileReset()">
<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd" />
</svg>
</button>
</div>
</div>
</div>
<!-- Progress Bar -->
<div class="relative pt-1" x-show="isUploading">
<div class="overflow-hidden h-2 mb-4 text-xs flex rounded bg-green-200">
<div :style="`width: ${progress}%`" class="shadow-none flex flex-col text-center whitespace-nowrap text-white justify-center bg-green-500"></div>
</div>
</div>
</div>
<?php
namespace App\Http\Livewire;
use Livewire\Component;
use Livewire\WithFileUploads;
class FileUpload extends Component
{
use WithFileUploads;
public $file;
public function render()
{
return view('livewire.file-upload');
}
public function updatedFile()
{
$this->validate([
'file' => 'file|max:8192', // 8MB Max
]);
\(this->emitUp('fileUploaded', \)this->file);
}
public function fileComplete()
{
\(this->emitUp('fileUpload', \)this->file->getRealPath());
}
public function fileReset()
{
$this->emitUp('fileReset');
}
}
<?php
namespace App\Http\Livewire;
use Livewire\Component;
use Livewire\TemporaryUploadedFile;
class ParentComponent extends Component
{
protected $listeners = [
'fileUpload',
'fileReset',
];
public $file;
public function fileUpload($payload)
{
\(this->file = new TemporaryUploadedFile(\)payload, config('filesystems.default'));
/* When you are ready to store the file */
/*
$this->file->storePubliclyAs(
'documents',
'sample',
config('filesystems.default'),
);
*/
}
public function fileReset()
{
$this->file = null;
}
}
Hopefully you can find this useful. I was banging my head against the wall for a couple hours with this, and naturally the solution was easier than anticipated.
