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"""
<div
:if={msg = render_slot(@inner_block) || Phoenix.Flash.get(@flash, @kind)}
id={@id}
phx-hook="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
π§ 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;
Hook it up in your LiveSocket
config:
import Hooks from "./hooks";
let liveSocket = new LiveSocket("/live", Socket, { hooks: { AutoClearFlash }, ... });
π¨ 3. Visual Countdown with Raw CSS
Inside your app.css
, add this keyframes
animation
@keyframes countdown {
from { width: 100%; }
to { width: 0%; }
}
β³ 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.
<div class="mt-2 h-1 w-full overflow-hidden rounded-full bg-white/30">
<div 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"
]} />
</div>
β 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!
Top comments (0)