Open Source Uses About
BaseCode

Uploading Files With Livewire

by Brian Faust – 2 minute read

When you first start working with Livewire you might be stumped for a moment when you find out that handling file uploads can be quite a challenge and isn’t as straight forward as most other things. There are 3 things we need to upload a file and display it, the Livewire component, the Blade template and a Vue component.

Livewire Component

The Livewire component in this scenario is purely for presentational purposes as the actual file upload has to be performed outside of it due to how Livewire works. The only job of the component is to listen for the refreshProfilePhoto event which will trigger a re-render of the component that is emitted by the Vue component.

<?php

namespace App\Http\Livewire;

use Livewire\Component;

class UpdateProfilePhoto extends Component
{
    use Concerns\InteractsWithUser;

    public ?string $photo = null;

    protected $listeners = [
        'refreshProfilePhoto' => 'render',
    ];
}

Blade Template

The blade template serves two purposes. It is responsible for rendering the photo of the user and the Vue component that takes care of uploading the photo if the user wishes to change it.

<div>
    <form class="spaced-y-4">
        <div class="mb-4 md:flex md:items-center">
            <span
                style="background-image: url({{ $this->user->photo }})"
                class="inline-block object-cover w-48 h-48 mx-auto rounded"></span>
        </div>

        <div class="flex items-center justify-between">
            <div class="relative overflow-hidden">
                <update-photo url="/app/user/photo" event="refreshProfilePhoto"></update-photo>
            </div>
        </div>
    </form>
</div>

Vue Component

The Vue Component is the most important part and responsible for performing the actual upload of the photo that the user chooses. How you handle the upload server-side is not important, what is important is that you call livewire.emit(this.event); after the uploads has been completed. The this.event variable should contain the event that your Livewire component is listening for so that the component gets re-rendered and gives the user smooth experience without a page reload.

<template>
    <div>
        <input
            ref="photo"
            type="file"
            class="absolute top-0 block opacity-0 cursor-pointer"
            name="photo"
            @change="uploadPhoto"
        />

        <div class="btn-primary">Update Photo</div>
    </div>
</template>

<script>
import axios from "axios";

export default {
    props: {
        url: {
            type: String,
            required: true
        },
        event: {
            type: String,
            required: true
        }
    },
    methods: {
        uploadPhoto(e) {
            e.preventDefault();

            if (!this.$refs.photo.files.length) {
                return;
            }

            const data = new FormData();
            data.append("photo", this.$refs.photo.files[0]);

            axios.post(this.url, data).then(
                () => {
                    livewire.emit(this.event);
                },
                error => {
                    this.errors = error.response.data.errors;
                }
            );
        }
    }
};
</script>

Conclusion

It can be quite difficult at first to figure out how to get file uploads working in combination with a Livewire component but once you figured it out it makes you realise how versatile Livewire is because you created an interactive UI without any page reloads with a combination of JavaScript and PHP instead of having a single Vue Component that requires you to add yet another API endpoint just to get the users information to render their photo.


More about laravel, livewire & files