skip to content

In a Livewire component, how do you show an upload progress bar and a cancel button while a receipt file is uploading?

level: juniorimportance: should knowfreq 32%

answer

  1. events dispatched on the input
  2. livewire-upload-progress: detail.progress
  3. start, finish, error, cancel
  4. wire:target the property name
  5. $cancelUpload('receipt')

basics

~10 s

Livewire dispatches livewire-upload-start, -progress, -finish, -error and -cancel browser events from the file input; an Alpine wrapper can read $event.detail.progress into a progress bar, and wire:click="$cancelUpload('receipt')" aborts the upload.

solid answer

~30 s

During an upload Livewire dispatches bubbling browser events on the `<input type="file">`: `livewire-upload-start`, `livewire-upload-progress` (with `event.detail.progress` as a percentage), `livewire-upload-finish`, `livewire-upload-error` and `livewire-upload-cancel`. A wrapping element with Alpine can listen with `x-on:livewire-upload-progress="progress = $event.detail.progress"` and bind it to a `<progress>` element. For a simple busy message, `wire:loading wire:target="receipt"` targets the property name and reacts to the upload itself. A cancel button uses `wire:click="$cancelUpload('receipt')"`, which aborts the request, fires the cancel event and clears the input.

go deeper

for a junior

Recall the upload event names and that progress arrives as a percentage in the event detail.

for a middle

Explain handling every end state, targeting the property with wire:loading, and $cancelUpload clearing the input.

for a senior

Block submission during uploads and design feedback that survives slow mobile connections and failures.

for a principal

Standardise upload UX components so every form handles progress, failure and cancel the same way.

## Why uploads need their own feedback A Livewire **file upload** is not an ordinary component request. The file goes to a separate temporary endpoint (or straight to S3), and a receipt photographed at high resolution on a slow mobile connection can take many seconds. Users need to see that something is happening and how far along it is, and they need a way to give up and choose another file. ## The upload events Livewire's JavaScript dispatches **custom DOM events** on the file input, and they bubble, so any ancestor can listen: | Event | When | Detail | |---|---|---| | `livewire-upload-start` | the upload begins | component id and property | | `livewire-upload-progress` | repeatedly during transfer | `progress`, a percentage | | `livewire-upload-finish` | the file is stored temporarily | component id and property | | `livewire-upload-error` | the upload fails (for example the global rules reject it) | component id and property | | `livewire-upload-cancel` | the upload was cancelled | component id and property | ## A progress bar with Alpine Alpine ships with Livewire, so a small wrapper holds the UI state: ```html <div x-data="{ uploading: false, progress: 0 }" x-on:livewire-upload-start="uploading = true" x-on:livewire-upload-finish="uploading = false" x-on:livewire-upload-error="uploading = false" x-on:livewire-upload-cancel="uploading = false" x-on:livewire-upload-progress="progress = $event.detail.progress"> <input type="file" wire:model="receipt"> <progress x-show="uploading" max="100" x-bind:value="progress"></progress> </div> ``` Listening for **all four** end states matters; forgetting error or cancel leaves the bar on screen forever after a failed upload. ## Simpler loading states When a percentage is not needed: - `wire:loading wire:target="receipt"` shows an element while the `receipt` upload is running; Livewire treats upload start and finish for that property as loading state. - `wire:loading.attr="disabled" wire:target="receipt"` on the Save button keeps the form from being submitted mid-upload. ## Cancelling `$cancelUpload('receipt')` is a Livewire magic action available in `wire:click` (and as `$wire.cancelUpload('receipt')` from Alpine). It aborts the in-flight upload, dispatches `livewire-upload-cancel` and clears the file input so the user can pick again. It does not touch files already uploaded and stored temporarily in earlier attempts. ## Multiple files For a `multiple` input the files are sent together in one upload, so the same events fire for the batch as a whole and the progress reflects the combined transfer. Showing one bar per file needs a separate input per file, or the lower-level JavaScript upload API. ## Common mistakes 1. Handling only `finish` and leaving the spinner up after an error. 2. Targeting the save action instead of the property in `wire:target`, so the indicator never shows during the upload itself. 3. Letting users submit the claim while an upload is still in progress; disable the Save button while `uploading` is true. 4. Expecting the progress event to fire for the later save request, which is an ordinary component request. ## Error handling that users understand When the upload endpoint rejects a file, for example because it exceeds the global size limit, Livewire fires `livewire-upload-error` in the browser and also reports a validation error on the bound property on the server. The progress UI should reset on the error event, and the view should show the message with the usual error display for `receipt`. Together these give the user a clear "this file is too large" instead of a bar frozen at some percentage. ## Why interviewers ask it Progress and cancel are small features, but they reveal whether a candidate knows that Livewire uploads run outside the normal request cycle. Someone who expects `wire:loading` on the save action to cover the upload, or who handles only the happy path, has probably not shipped an upload form that real users on slow connections rely on.

  • Why should the Save button be disabled while an upload is in progress?
    Until `livewire-upload-finish` fires, the property does not yet hold the temporary file. Submitting early runs the save action with a missing or previous file, so validation fails or the wrong receipt is stored. Tie the button's disabled state to the uploading flag.
  • Does wire:loading wire:target="receipt" react to the upload or only to later requests?
    It reacts to the upload. Livewire listens for the upload start and finish events for that property and treats them as loading state, in addition to any ordinary request that updates the property.

The upload events work like a parcel tracking page: 'picked up', a running 'in transit' percentage, then exactly one of 'delivered', 'failed' or 'returned to sender', and the page must handle every final state, not just 'delivered'.

saying these in an interview costs you the question

  • Listens only for the finish event and ignores error and cancel.
  • Targets the save action in wire:target to show upload progress.
  • Expects the progress event to carry bytes rather than a percentage.
  • Believes a cancelled upload deletes previously stored temporary files.