I think the issue is that wire:loading.class.remove is doing the opposite of what you want here.
This:
wire:loading.class.remove="btn btn-disabled"
removes those classes only while Livewire is loading, then adds them back once the request is finished. So your button ends up disabled again after the upload completes.
Instead, start with the button enabled and only disable it while the upload is in progress:
<input type="file" wire:model="file">
<button type="submit" class="btn btn-sm btn-success">
Upload file
</button>
<button
type="button"
wire:click="saveMetadata"
class="btn btn-sm btn-primary"
wire:loading.attr="disabled"
wire:loading.class="btn-disabled"
wire:target="file"
>
Save Metadata
</button>
The important parts are:
wire:loading.attr="disabled"
wire:target="file"
wire:loading.attr="disabled" actually disables the button during loading, while wire:target="file" makes sure the loading state is tied specifically to the file upload property.
Also, if this button is inside a form, I’d explicitly set type="button" on the metadata button so it doesn’t accidentally submit the form.
One more note: if your upload button calls a Livewire method instead of relying directly on wire:model, you can target that method instead:
<button
type="button"
wire:click="saveMetadata"
class="btn btn-sm btn-primary"
wire:loading.attr="disabled"
wire:loading.class="btn-disabled"
wire:target="uploadFile"
>
Save Metadata
</button>
So the general idea is: use wire:loading.attr="disabled" to disable the button during the upload, and use wire:target to make sure the loading state is only tied to the upload, not to every Livewire request.