PhxMediaLibrary.Components (PhxMediaLibrary v0.6.1)

Copy Markdown View Source

Ready-to-use Phoenix LiveView components for media uploads and galleries.

These components eliminate the boilerplate of building file upload UIs in Phoenix LiveView. They handle drag-and-drop, progress bars, image previews, error display, and existing media management out of the box.

Setup

Add to your my_app_web.ex html_helpers:

defp html_helpers do
  quote do
    # ... existing imports
    import PhxMediaLibrary.Components
    import PhxMediaLibrary.ViewHelpers
  end
end

Quick Start

# In your LiveView mount:
def mount(_params, _session, socket) do
  use PhxMediaLibrary.LiveUpload

  post = Posts.get_post!(id)

  {:ok,
   socket
   |> assign(:post, post)
   |> allow_media_upload(:images, model: post, collection: :images)
   |> stream_existing_media(:media, post, :images)}
end

# In your template:
<.media_upload
  upload={@uploads.images}
  id="post-images-upload"
/>

<.media_gallery
  media={@streams.media}
  id="post-gallery"
/>

Summary

Functions

Renders a BlurHash placeholder as a <canvas> element.

Renders a gallery of existing media items with delete support.

Renders a complete media upload zone with drag-and-drop, previews, and progress.

Renders a minimal file input button without the full drop zone.

Renders a <video> player for a media item with an optional poster frame and a metadata strip showing duration, dimensions, and codec.

Functions

blurhash(assigns)

Renders a BlurHash placeholder as a <canvas> element.

The hash is decoded client-side by a colocated JavaScript hook that paints the low-fidelity blurred preview onto the canvas. This is a lightweight alternative to the tiny JPEG placeholder: the hash is ~20–40 bytes stored directly in the database rather than a base64-encoded image.

Requires PhxMediaLibrary.Config.blurhash_enabled?/0 to be true (opt-in via config :phx_media_library, responsive_images: [blurhash: true]) and the :image library to be available.

Attributes

  • :media — (required) a PhxMediaLibrary.Media struct. The hash is read from media.responsive_images["blurhash"].
  • :width — canvas render width in pixels (default: 32). The canvas is stretched to fill its container via width: 100% CSS so you can set this to any small value without affecting the visual size.
  • :height — canvas render height in pixels (default: 32). If you pass nil, the component preserves the aspect ratio from the media metadata.
  • :class — additional CSS classes applied to the <canvas>.

Examples

<%!-- Basic usage --%>
<PhxMediaLibrary.Components.blurhash media={@photo} />

<%!-- Full-bleed cover with aspect-ratio preservation --%>
<div class="relative overflow-hidden rounded-xl">
  <PhxMediaLibrary.Components.blurhash
    media={@photo}
    class="absolute inset-0 w-full h-full object-cover"
  />
  <img src={PhxMediaLibrary.url(@photo)} class="relative w-full" loading="lazy" />
</div>

Attributes

  • media (:any) (required)
  • width (:integer) - Defaults to 32.
  • height (:integer) - Defaults to 32.
  • class (:string) - Defaults to nil.

media_gallery(assigns)

Renders a gallery of existing media items with delete support.

Designed to work with LiveView streams. Each media item is rendered as a card with a thumbnail (for images) or file icon (for documents), the filename, file size, and a delete button.

Attributes

  • media (required) — the stream from @streams.media
  • id (required) — unique DOM id for the gallery container
  • class — additional CSS classes
  • conversion — the conversion to display for image thumbnails (default: nil = original)
  • delete_event — the phx-click event name for deleting (default: "delete_media")
  • delete_target — optional phx-target for the delete event
  • confirm_delete — show a JS confirmation before delete (default: true)
  • confirm_message — the confirmation prompt text
  • columns — grid column count hint: 2, 3, 4, 5, 6 (default: 4)

Slots

  • item — override rendering of each media item. Receives the media struct.
  • empty — content to show when the gallery is empty.

Examples

<%!-- Basic usage with streams --%>
<.media_gallery
  media={@streams.media}
  id="post-gallery"
/>

<%!-- With custom columns and conversion --%>
<.media_gallery
  media={@streams.media}
  id="post-gallery"
  conversion={:thumb}
  columns={3}
/>

<%!-- Custom item rendering --%>
<.media_gallery media={@streams.media} id="gallery">
  <:item :let={{_id, media}}>
    <div class="relative aspect-square rounded-lg overflow-hidden">
      <img src={PhxMediaLibrary.url(media, :thumb)} class="object-cover w-full h-full" />
    </div>
  </:item>
  <:empty>
    <p class="text-zinc-400 text-center py-12">No media uploaded yet.</p>
  </:empty>
</.media_gallery>

Attributes

  • media (:any) (required) - the stream from @streams.
  • id (:string) (required) - unique DOM id for the gallery container.
  • class (:string) - additional CSS classes. Defaults to nil.
  • conversion (:atom) - image conversion for thumbnails. Defaults to nil.
  • delete_event (:string) - event name for delete. Defaults to "delete_media".
  • delete_target (:any) - phx-target for delete event. Defaults to nil.
  • confirm_delete (:boolean) - show confirmation dialog. Defaults to true.
  • confirm_message (:string) - confirm text. Defaults to "Are you sure you want to delete this file?".
  • columns (:integer) - grid columns (2-6). Defaults to 4.

Slots

  • item - override item rendering — receives {dom_id, media}.
  • empty - content when gallery is empty.

media_upload(assigns)

Renders a complete media upload zone with drag-and-drop, previews, and progress.

This component renders:

  • A drop zone with a file picker button
  • Live image previews for selected files
  • Upload progress bars per entry
  • Error messages for invalid files
  • Cancel buttons for pending uploads

The parent LiveView must call allow_upload/3 or allow_media_upload/3 and handle at minimum a "validate" event (can be a no-op) and a submit event that calls consume_media/5 or consume_uploaded_entries/3.

Attributes

  • upload (required) — the upload config from @uploads.name
  • id (required) — unique DOM id for the component
  • label — text label above the drop zone
  • sublabel — secondary text below the icon (e.g. accepted formats)
  • class — additional CSS classes on the outer wrapper
  • compact — render a compact single-line version (default: false)
  • disabled — disable the upload zone (default: false)
  • cancel_event — the phx-click event name for cancelling an entry (default: "cancel_upload")
  • cancel_target — optional phx-target for the cancel event

Slots

  • drop_zone — override the default drop zone content entirely

Examples

<%!-- Basic usage --%>
<.media_upload upload={@uploads.images} id="images-upload" />

<%!-- With labels --%>
<.media_upload
  upload={@uploads.images}
  id="images-upload"
  label="Upload Images"
  sublabel="JPG, PNG, WebP up to 10MB"
/>

<%!-- Compact variant for single-file uploads --%>
<.media_upload
  upload={@uploads.avatar}
  id="avatar-upload"
  compact={true}
  label="Profile Photo"
/>

<%!-- Custom drop zone content --%>
<.media_upload upload={@uploads.docs} id="docs-upload">
  <:drop_zone>
    <div class="text-center p-8">
      <p class="text-lg font-semibold">Drop your documents here</p>
    </div>
  </:drop_zone>
</.media_upload>

Attributes

  • upload (:any) (required) - the upload config from @uploads.
  • id (:string) (required) - unique DOM id.
  • label (:string) - label text above the drop zone. Defaults to nil.
  • sublabel (:string) - secondary helper text. Defaults to nil.
  • class (:string) - additional CSS classes. Defaults to nil.
  • compact (:boolean) - compact single-line layout. Defaults to false.
  • disabled (:boolean) - disable the upload zone. Defaults to false.
  • cancel_event (:string) - event name for cancel. Defaults to "cancel_upload".
  • cancel_target (:any) - phx-target for cancel event. Defaults to nil.

Slots

  • drop_zone - override default drop zone content.

media_upload_button(assigns)

Renders a minimal file input button without the full drop zone.

Useful for embedding uploads inline within forms or other UI elements where the full drop zone is too large.

Attributes

  • upload (required) — the upload config from @uploads.name
  • id (required) — unique DOM id
  • label — button label text (default: "Choose file")
  • class — additional CSS classes on the wrapper
  • icon — hero icon name (default: "hero-arrow-up-tray")

Examples

<.media_upload_button upload={@uploads.avatar} id="avatar-btn" label="Change photo" />

Attributes

  • upload (:any) (required) - the upload config from @uploads.
  • id (:string) (required) - unique DOM id.
  • label (:string) - button label. Defaults to "Choose file".
  • class (:string) - additional CSS classes. Defaults to nil.
  • icon (:string) - icon name. Defaults to "hero-arrow-up-tray".

media_video(assigns)

Renders a <video> player for a media item with an optional poster frame and a metadata strip showing duration, dimensions, and codec.

Poster frames are generated automatically when FFmpeg is installed and the video was uploaded after v0.6.0. When no poster is available the browser renders its default video thumbnail.

Examples

<PhxMediaLibrary.Components.media_video media={@video} />

<PhxMediaLibrary.Components.media_video
  media={@video}
  controls={true}
  class="rounded-xl shadow-lg"
/>

Attributes

  • media (:any) (required) - A PhxMediaLibrary.Media struct for a video file.
  • class (:string) - Extra CSS classes for the wrapping div. Defaults to nil.
  • controls (:boolean) - Show native browser video controls. Defaults to true.
  • autoplay (:boolean) - Auto-play on load. Defaults to false.
  • muted (:boolean) - Mute audio on load (required for autoplay in most browsers). Defaults to false.
  • loop (:boolean) - Loop the video. Defaults to false.