LiveView gives us beautiful declarative ways to build interactive UIs without writing much JS. But one thing developers often want is this:

✅ A flash message that shows up, waits a few seconds, fades away — and shows a countdown line while it's visible.

Here’s how to do it with almost no JS and a sprinkle of Tailwind + raw CSS.


🔧 1. Add the Flash Component

In your LiveView or component module (core_components.ex), define a flash like this if it doesn't exist already:

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

  ~H"""
  AutoClearFlash"
    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-sky-50 text-blue-500 ring-blue-500 fill-blue-900",
      @kind == :success && "bg-emerald-50 text-emerald-800 ring-emerald-500 fill-cyan-900",
      @kind == :warn && "bg-amber-50 text-amber-900 shadow-md ring-amber-500 fill-amber-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 == :success} name="hero-check-circle-mini" class="h-4 w-4" />
      <.icon :if={@kind == :warn} name="hero-exclamation-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



    Enter fullscreen mode
    


    Exit fullscreen mode
    





  
  
  🧠 2. Auto-dismiss via Hook
Add a small JS hook (hooks.js):

let Hooks = {}
Hooks.AutoClearFlash = {
  mounted() {
    const ignoredIDs = ["client-error", "server-error"];
    if (ignoredIDs.includes(this.el.id)) return;

    setTimeout(() => this.el.click(), 2500);
  }
};

export default Hooks;



    Enter fullscreen mode
    


    Exit fullscreen mode
    




Hook it up in your LiveSocket config:

import Hooks from "./hooks";
let liveSocket = new LiveSocket("/live", Socket, { hooks: { AutoClearFlash }, ... });



    Enter fullscreen mode
    


    Exit fullscreen mode
    





  
  
  🎨 3. Visual Countdown with Raw CSS
Inside your app.css, add this keyframes animation


@keyframes countdown {
  from { width: 100%; }
  to { width: 0%; }
}



    Enter fullscreen mode
    


    Exit fullscreen mode
    





  
  
  ⏳ 4. Countdown bar
Expand your flash component with this countdown bar for beautiful visual effect. Add it just at the end of your flash component.

 class="mt-2 h-1 w-full overflow-hidden rounded-full bg-white/30">
   class={[
    "h-full animate-[countdown_2.5s_linear_forwards]",
    @kind == :info && "bg-blue-500",
    @kind == :success && "bg-emerald-500",
    @kind == :warn && "bg-amber-500",
    @kind == :error && "bg-rose-500"
  ]} />




    Enter fullscreen mode
    


    Exit fullscreen mode
    





  
  
  ✅ That's It!
You now have:
A flash that disappears after 2.5s

A smooth fade-out animation

A subtle countdown bar like a fuse burning out

Let LiveView do the heavy lifting ✨💬 Got ideas for variations (e.g. pause on hover, different timers per flash type)? Let me know in the comments!