A Real Estate Client’s Property Video Upload Broke My Livewire Form, Here’s What I Learned

A real estate agent I was building a property listing site for wanted agents to upload photos and short walkthrough videos directly from their phones while standing inside the property. Sounds convenient, right? It is, until someone tries uploading a 45-second video shot in 4K on an iPhone, which easily hits 150MB, and your form just times out with a vague error message.

I genuinely spent an entire afternoon convinced my server was broken. Restarted PHP, checked Nginx configs, googled every possible cause. Turns out the actual issue was a stack of default limits, PHP’s upload_max_filesize, Livewire’s own upload restrictions, and my web server’s request size limits, all quietly capping things way below what I actually needed.

If you’ve ever built a file upload in Livewire that worked perfectly for small test files but mysteriously failed on anything bigger, this is exactly the rabbit hole I went down, and exactly how I climbed back out of it.

Why Livewire File Uploads Feel Different From Normal Forms

Regular HTML forms just send files along with everything else when you hit submit. Livewire does something a bit different behind the scenes, it uploads the file to a temporary location immediately when you select it, before you even submit the form. This is actually a nice feature since it means users see upload progress right away instead of waiting until the very end.

But it also means there are more layers where things can go wrong, temporary storage limits, upload endpoint timeouts, and validation happening at a slightly different point than you’d expect coming from plain Laravel forms.

Step 1: The Basic Setup (Where Most Tutorials Stop)

php artisan make:livewire PropertyUpload
use Livewire\WithFileUploads;

class PropertyUpload extends Component
{
    use WithFileUploads;

    public $photos = [];
    public $video;

    public function save()
    {
        $this->validate([
            'photos.*' => 'image|max:5120',
            'video' => 'file|mimes:mp4,mov|max:20480',
        ]);

        foreach ($this->photos as $photo) {
            $photo->store('properties/photos', 'public');
        }

        if ($this->video) {
            $this->video->store('properties/videos', 'public');
        }
    }

    public function render()
    {
        return view('livewire.property-upload');
    }
}

Most tutorials stop right here and call it done. This works fine for small test uploads, a few photos under 5MB, maybe a short 10MB video clip. It completely falls apart the moment someone tries uploading anything realistically sized for actual property media, which is exactly what happened during my client’s first real test.

Step 2: Understanding Where the Actual Limits Live

Here’s what I didn’t fully understand at first, there isn’t just one file size limit, there are several, and they all need to agree with each other or the smallest one wins.

PHP’s own configuration (php.ini) has two settings that matter here:

upload_max_filesize = 20M
post_max_size = 25M

Notice post_max_size needs to be slightly bigger than upload_max_filesize, since the entire request (file plus other form data) needs to fit under that limit.

Livewire’s own temporary upload limit, set in config/livewire.php:

'temporary_file_upload' => [
    'rules' => 'file|max:20480', // in kilobytes
],

This one genuinely tripped me up the most, because even after increasing PHP’s limits, uploads still failed until I realized Livewire has its own separate cap on temporary uploads that needs adjusting too.

Your web server’s request size limit. If you’re using Nginx (which is what I was running for this client’s VPS), there’s a separate setting:

client_max_body_size 25M;

Miss this one and you’ll get a fairly unhelpful 413 error with barely any explanation, which is actually what confused me the most during that afternoon of debugging. The Laravel logs showed nothing useful because the request never even reached Laravel, Nginx rejected it before PHP got involved at all.

Step 3: Actually Fixing It For Large Files

Once I understood all three layers needed to match, here’s what I set for the real estate project, allowing videos up to roughly 100MB:

upload_max_filesize = 100M
post_max_size = 105M
'temporary_file_upload' => [
    'rules' => 'file|max:102400', // 100MB in KB
],
client_max_body_size 105M;

After restarting PHP-FPM and Nginx, the same video that failed before uploaded without a single issue.

Step 4: Adding a Real Progress Indicator

Once uploads actually worked, the next problem was user experience. A 100MB video upload on average WiFi genuinely takes a while, and without any visible feedback, agents kept thinking the form had frozen.

Livewire gives you built-in upload progress tracking:

<input type="file" wire:model="video">

<div wire:loading wire:target="video">
    <div class="w-full bg-gray-200 rounded h-2">
        <div class="bg-blue-500 h-2 rounded" x-bind:style="'width: ' + $wire.uploadProgress + '%'"></div>
    </div>
    <span x-text="$wire.uploadProgress + '% uploaded'"></span>
</div>

This actually needs a small JavaScript event listener too, since uploadProgress isn’t automatically tracked without hooking into Livewire’s upload events:

Livewire.on('livewire-upload-progress', (event) => {
    // event.detail.progress gives you the percentage
});

Once agents could actually see a progress bar moving, the confused “is this broken” messages from the client’s team stopped completely.

Step 5: Validating File Type Properly (Not Just By Extension)

Here’s a mistake I made early on that I’m honestly a bit embarrassed about. I validated video files just by extension using mimes:mp4,mov, assuming that was solid enough. Then one agent uploaded a file that had a .mp4 extension but was actually a corrupted file, not a real video at all, and it passed validation just fine before failing later when trying to actually process or display it.

Laravel’s mimes rule does check the actual file signature, not just the extension, so this specific scenario turned out to be a different issue, a genuinely corrupted upload from a spotty hotel WiFi connection. But it made me realize I should add extra safety checks for file integrity, not just rely on Laravel’s validation alone for anything users will be viewing later.

For photos specifically, I added dimension validation too, since a few agents tried uploading tiny thumbnail-sized images that looked terrible once displayed on the actual listing page:

$this->validate([
    'photos.*' => 'image|max:5120|dimensions:min_width=800,min_height=600',
]);

Step 6: Cleaning Up Temporary Uploads

Something that genuinely surprised me, Livewire stores those temporary uploaded files even if the user never actually submits the form. If someone selects a video, then closes the tab without submitting, that file just sits in your storage/app/livewire-tmp folder indefinitely.

Laravel actually handles cleanup automatically through a scheduled command, but only if your scheduler is actually running. I hadn’t set up the cron job properly on that particular VPS initially, and after a couple weeks of testing, that temp folder had grown to a genuinely alarming size.

Make sure this is in your routes/console.php or scheduler setup (Laravel 11/12 moved scheduling here by default):

Schedule::command('livewire:cleanup-temp')->daily();

And obviously, make sure your actual cron job is running on the server:

* * * * * php /path-to-your-project/artisan schedule:run >> /dev/null 2>&1

Common Mistakes I’d Genuinely Warn You About

Only fixing one of the three size limits. I mentioned this already, but it deserves repeating because it’s the single most common reason people think Livewire uploads are “broken” when really it’s just a mismatch between PHP, Livewire’s config, and the web server.

Not testing on an actual mobile connection. Everything worked flawlessly on my laptop’s fast office WiFi. The moment an agent tested this from an actual property with weak signal, uploads timed out entirely. I ended up adding a friendlier timeout error message and suggesting agents upload media once they’re back on stable WiFi, rather than trying to force a fix that wasn’t realistic given actual field conditions.

Forgetting to validate array uploads properly. When allowing multiple photo uploads, remember it’s photos.* in your validation rules, not just photos. I forgot the asterisk once early on and validation silently didn’t apply to individual files in the array at all.

Not setting a reasonable max file count. I initially let agents upload unlimited photos per property. One agent uploaded genuinely over 200 images for a single listing, which made the listing page painfully slow to load. Adding a simple check limiting uploads to a sensible number, I went with 20 photos per property, fixed this without frustrating anyone since that’s still plenty for a property listing.

public function updatedPhotos()
{
    if (count($this->photos) > 20) {
        $this->photos = array_slice($this->photos, 0, 20);
        session()->flash('error', 'Maximum 20 photos allowed per listing.');
    }
}

Where This Actually Matters Beyond Real Estate

Since fixing this for the real estate project, I’ve run into the exact same size limit puzzle on:

  • A freelance portfolio site where designers uploaded large PSD and video demo reels
  • A local college’s assignment submission portal, where students uploaded fairly large PDF and video presentation files
  • An event photography business’s client gallery upload feature for full-resolution wedding photos

Every single one of these needed the same three-layer fix, PHP config, Livewire’s own limit, and the web server’s request size cap, all lined up together.

Final Thoughts

Handling large file uploads in Livewire isn’t actually complicated once you understand there are multiple layers involved, but it’s genuinely confusing the first time you hit it because the error messages rarely point you to the real cause. If your upload works fine for tiny test files but silently fails or times out on anything realistically sized, check all three limits before assuming your code is broken.

And build in a proper progress indicator from the start. Users, especially ones uploading large files over average mobile data, need to actually see something is happening. I learned that one from a slightly frustrated phone call with the client’s office manager, and it’s a lesson that’s stuck with me on every upload feature I’ve built since.

Leave a Reply

Your email address will not be published. Required fields are marked *