File upload

New

Overview

A LiveView file upload dropzone with progress, validation errors, previews, and removal controls.

FileUpload styles the native LiveView file input and renders the entries in its UploadConfig. LiveView owns selection, drag and drop, validation, progress, temporary storage, and external uploads.

Call allow_upload/3 in the parent LiveView, place the component inside a form with phx-change, handle cancel-upload, and consume completed entries. This example uses auto_upload: true with a progress callback, so it does not need a submit button. Uploads that wait for form submission can consume entries in phx-submit instead. The same component works with LiveView’s default temporary files, external uploads, or a custom streaming writer. Read the LiveView upload guide for setup, validation, and storage options. For an example that streams entries through a custom writer, see Streaming uploads with LiveView.

LiveView
@impl Phoenix.LiveView
def mount(_params, _session, socket) do
  {:ok,
   allow_upload(socket, :project_files,
     accept: ~w(.jpg .jpeg .png),
     max_entries: 3,
     max_file_size: 5_000_000,
     progress: &handle_progress/3,
     auto_upload: true
   )}
end

@impl Phoenix.LiveView
def handle_event("validate-file-upload", _params, socket), do: {:noreply, socket}

def handle_event("cancel-upload", %{"ref" => ref}, socket) do
  {:noreply, cancel_upload(socket, :project_files, ref)}
end

defp handle_progress(:project_files, entry, socket) do
  if entry.done? do
    uploaded_file =
      consume_uploaded_entry(socket, entry, fn %{path: path} ->
        destination = Path.join(["priv", "static", "uploads", entry.uuid <> Path.extname(entry.client_name)])
        File.cp!(path, destination)
        {:ok, destination}
      end)

    {:noreply, update(socket, :uploaded_files, &(&1 ++ [uploaded_file]))}
  else
    {:noreply, socket}
  end
end

The preview slot receives each UploadEntry. This example uses live_img_preview/1 and automatically consumes completed uploads; omit the slot to show the default file icon.

HEEx
<Form.form
  for={@form}
  id="project-files-form"
  phx-change="validate-file-upload"
  class="mx-auto w-full max-w-lg"
>
  <FileUpload.file_upload
    upload={@uploads.project_files}
    label="Project images"
    sublabel="(optional)"
  >
    <:preview :let={entry}>
      <.live_img_preview entry={entry} alt="" />
    </:preview>
  </FileUpload.file_upload>
</Form.form>

Auto Upload

Set auto_upload: true to start uploading files when they are selected. Otherwise, LiveView waits until the form is submitted. The example below uses auto_upload: false and includes a submit button. See allow_upload/3 for the remaining upload options.

HEEx
<Form.form
  for={@form}
  id="manual-project-files-form"
  phx-change="validate-file-upload"
  phx-submit="save-manual-upload"
  class="mx-auto w-full max-w-lg"
>
  <FileUpload.file_upload
    upload={@uploads.manual_project_files}
    label="Project images"
    cancel_event="cancel-manual-upload"
  >
    <:preview :let={entry}>
      <.live_img_preview entry={entry} alt="" />
    </:preview>
  </FileUpload.file_upload>
  <div class="flex justify-end">
    <Button.button type="submit">Upload images</Button.button>
  </div>
</Form.form>

Validation and Errors

Upload constraints are configured with allow_upload/3. The error slot receives each validation reason so applications can replace the default messages. This example accepts up to two PNG files no larger than 100 KB each.

HEEx
<Form.form
  for={@form}
  id="validation-files-form"
  phx-change="validate-file-upload"
  class="mx-auto w-full max-w-lg"
>
  <FileUpload.file_upload
    upload={@uploads.validation_files}
    label="PNG files"
    cancel_event="cancel-validation-upload"
  >
    <:error :let={reason}>
      {case reason do
        :too_many_files -> "Choose no more than two files."
        :too_large -> "Choose a PNG file smaller than 100 KB."
        :not_accepted -> "Choose a PNG file."
        _ -> "The file could not be uploaded."
      end}
    </:error>
  </FileUpload.file_upload>
</Form.form>

Document Upload

Omit the preview slot for non-image uploads to use the default file icon. This example accepts PDF files and waits for the form to be submitted before uploading them.

HEEx
<Form.form
  for={@form}
  id="document-files-form"
  phx-change="validate-file-upload"
  phx-submit="save-document-upload"
  class="mx-auto w-full max-w-lg"
>
  <FileUpload.file_upload
    upload={@uploads.document_files}
    label="Documents"
    cancel_event="cancel-document-upload"
  />
  <div class="flex justify-end">
    <Button.button type="submit">Upload documents</Button.button>
  </div>
</Form.form>

Add this module

terminal
mix dizzy.add file_upload

Component API

FileUpload.file_upload

Attributes

Attributes for FileUpload.file_upload
NameTypeDefault Values
cancel_event :string "cancel-upload"β€”
cancel_target :any nilβ€”
class :any nilβ€”
description :string nilβ€”
disabled :boolean falseβ€”
id :string nilβ€”
label :string Requiredβ€”
required :boolean falseβ€”
rest :global nilβ€”
sublabel :string nilβ€”
upload {:struct, Phoenix.LiveView.UploadConfig} Requiredβ€”

Slots

Slots for FileUpload.file_upload
NameRequired
error Optional
preview Optional

Explore Dizzy