Image upload component for PhiaUI.
Provides image_upload/1 — a styled wrapper over Phoenix LiveView's native
file upload system (live_file_input). Renders a click-to-upload drop zone,
live image previews via live_img_preview/1, per-entry progress bars,
per-entry error messages, and entry removal buttons.
This component uses zero custom JavaScript — all upload behaviour is handled by Phoenix LiveView's built-in upload machinery. No JS hook is needed.
When to use
Use image_upload/1 for any image attachment field in a form:
- Profile / avatar upload
- Product image gallery
- Article cover image
- Document attachment (with accept: ~w(.pdf .docx .xlsx))
- Receipt / invoice photo capture
Requirements
The parent LiveView must configure the upload with allow_upload/3 in mount/3:
def mount(_params, _session, socket) do
{:ok,
socket
|> allow_upload(:avatar,
accept: ~w(.jpg .jpeg .png .webp .gif),
max_entries: 1,
max_file_size: 5_242_880 # 5 MB
)}
endThen pass @uploads.avatar as the :upload attribute.
Basic example
<.form for={@form} phx-submit="save_profile" phx-change="validate">
<.image_upload
upload={@uploads.avatar}
label="Drag & drop or click to upload a profile photo"
/>
<.button type="submit">Save</.button>
</.form>Multiple files example
def mount(_params, _session, socket) do
{:ok,
socket
|> allow_upload(:product_images,
accept: ~w(.jpg .jpeg .png .webp),
max_entries: 6,
max_file_size: 10_000_000
)}
end
<.image_upload
upload={@uploads.product_images}
label="Upload up to 6 product images"
/>Handling entry cancellation
Add this event handler to support the entry removal (×) button:
def handle_event("cancel_upload", %{"ref" => ref}, socket) do
{:noreply, cancel_upload(socket, :avatar, ref)}
endThe upload name (:avatar) must match the atom used in allow_upload/3.
Consuming uploads after form submission
After a successful phx-submit, consume entries with consume_uploaded_entries/3:
def handle_event("save_profile", _params, socket) do
urls =
consume_uploaded_entries(socket, :avatar, fn %{path: tmp_path}, _entry ->
dest = Path.join([:code.priv_dir(:my_app), "static", "uploads",
Path.basename(tmp_path) <> ".jpg"])
File.cp!(tmp_path, dest)
{:ok, "/uploads/" <> Path.basename(dest)}
end)
{:noreply, assign(socket, profile_image_url: List.first(urls))}
endError messages
The component maps Phoenix LiveView upload error atoms to English strings:
| Atom | Message |
|---|---|
:too_large | "File too large" |
:not_accepted | "Unsupported file format" |
:too_many_files | "File limit reached" |
| other | "Upload error" |
Summary
Functions
Renders an image upload zone integrated with Phoenix LiveView's upload system.
Functions
Renders an image upload zone integrated with Phoenix LiveView's upload system.
Renders:
- A click-to-upload drop zone (a
<label>wrappinglive_file_input/1) - A list of accepted entries — each with a thumbnail preview, filename, progress bar, and a remove button
- Per-entry error messages for validation failures
The drop zone is hidden from the visible file input (sr-only) so the
entire styled <label> area acts as the upload trigger. This approach
requires no custom JS — it is standard HTML form behaviour.
Attributes
upload(:any) (required) - APhoenix.LiveView.UploadConfigstruct returned by@uploads.field_name. Provides entry list, progress, errors, and the file input reference.label(:string) - Instructional text displayed inside the drop zone. Defaults to"Click or drag & drop to upload".class(:string) - Additional CSS classes for the outer wrapper div. Defaults tonil.