Recently, I saw a discussion on Bluesky about making flash messages in Phoenix LiveView disappear automatically:

Elixir Forum post "How to make flash message disappear after 5 seconds" created in 2020, with the latest reply in April 2025, remains unresolved.

It turns out this is something I already solved a while ago. Let me walk you through how I implemented automatic flash dismissal in my own project.

Step 1: create a custom hook

First, I created a new file at assets/js/hooks/auto-dismiss-flash.js (note: I prefer keeping one file per hook for clarity) with the following code:

export default {
  mounted() {
    setTimeout(() => {
      this.el.style.transition = "opacity 0.5s";
      this.el.style.opacity = "0";
      setTimeout(() => this.el.remove(), 500);
    }, 5000); // Wait 5 seconds before starting to fade out
  },
};

This hook:

  • Waits 5 seconds,
  • Fades out the flash message over 0.5 seconds,
  • Then removes it from the DOM.

Step 2: register the Hook in app.js

Next, I updated assets/js/app.js to load and register the new hook:

import AutoDismissFlash from "./hooks/auto-dismiss-flash";

const Hooks = {
  AutoDismissFlash,
};

const liveSocket = new LiveSocket("/live", Socket, {
  hooks: Hooks,
  longPollFallbackMs: 2500,
  params: { _csrf_token: csrfToken },
});

Now the hook is available for use anywhere in my LiveView templates.

Step 3: attach the hook to the flash Component

Finally, I updated my flash component in lib/myapp_web/components/core_components.ex to use the AutoDismissFlash hook:

@doc """
Renders flash notices.

## Examples

    <.flash kind={:info} flash={@flash} />
    <.flash kind={:error} flash={@flash} />
"""
attr :id, :string, doc: "Optional id for the flash container"
attr :flash, :map, default: %{}, doc: "The map of flash messages to display"
attr :title, :string, default: nil
attr :kind, :atom, values: [:info, :error], doc: "Used for styling and flash lookup"
attr :rest, :global, doc: "Additional HTML attributes for the flash container"

slot :inner_block, doc: "Optional custom content for the flash message"

def flash(assigns) do
  assigns = assign_new(assigns, :id, fn -> "flash-#{assigns.kind}" end)

  ~H"""
  <div
    :if={msg = render_slot(@inner_block) || Phoenix.Flash.get(@flash, @kind)}
    id={@id}
    phx-hook="AutoDismissFlash"
    phx-click={JS.push("lv:clear-flash", value: %{key: @kind}) |> hide("##{@id}")}
    role="alert"
    class={[
      "fixed top-2 right-2 mr-2 w-80 sm:w-96 z-50 rounded-lg p-3 ring-1",
      @kind == :info && "bg-emerald-50 text-emerald-800 ring-emerald-500 fill-cyan-900",
      @kind == :error && "bg-rose-50 text-rose-900 shadow-md ring-rose-500 fill-rose-900"
    ]}
    {@rest}
  >
    <p :if={@title} class="flex items-center gap-1.5 text-sm font-semibold leading-6">
      <.icon :if={@kind == :info} name="hero-information-circle-mini" class="h-4 w-4" />
      <.icon :if={@kind == :error} name="hero-exclamation-circle-mini" class="h-4 w-4" />
      <%= @title %>
    </p>
    <p class="mt-2 text-sm leading-5"><%= msg %></p>
    <button type="button" class="group absolute top-1 right-1 p-2" aria-label={gettext("close")}>
      <.icon name="hero-x-mark-solid" class="h-5 w-5 opacity-40 group-hover:opacity-70" />
    </button>
  </div>
  """
end

By adding phx-hook="AutoDismissFlash" to the container, each flash message now automatically fades out and disappears without any further user interaction.

Sources