Live Toast - new toast/flash component for Phoenix LiveView

Announcing Live Toast: a replacement toast/flash component for Phoenix LiveView, heavily inspired by the look of Sonner (the amazing toast component for React).

I’ve been cooking this up for a while. It’s still pre-1.0, but the general API should be solid.

My biggest gripe with the default flash system in Phoenix is that you may only have one toast for a given severity level. No more! Now you can use Live Toast to get around this, but it will also continue to render your flash messages in the same style so you can incrementally move towards the toasts instead.

As I said, its still early days so let me know if you find any problems.

Demo: https://toast.src.rip
Hex Docs: Live Toast — Live Toast v0.7.0
GitHub:

43 Likes

This is great! It looked promising when you first mentioned it and I’m into the further work you’ve done.

I’m in the processes of converting a project over to it. A couple of problems off the bat which are only related to docs: you aren’t properly indenting the @doc examples so they aren’t rendering as code in Hexdocs. The code link (</>) is also broken in a bunch (possibly all) places. Not sure why that one is.

Lastly: I would love a :bottom_center option (and of course probably include :top_center) as that’s what I’m currently using. Happy to send a PR.

Oh, also while it’s fairly obvious, the docs mention using the put_toast helper without any instructions on how to import it.

EDIT: I’m just going to create github issues.

3 Likes

Thank you, GitHub issues are much appreciated!

In terms of importing, you really only need the top level LiveToast module so you can use it without aliasing or imports!

Oh I figured that out, just nice to include in the docs as it’s not 100% clear and causes a bit of mental dissonance :slight_smile: Or even like just put LiveToast.put_toast so it’s super obvious it’s not in a submodule or something. I just appreciate idiot-proof docs :grin:

PS: Client project has been converted to LiveToast and it’s working like a charm :rocket:

3 Likes

Nicely done :pray:. I’ve done a few Toast versions in Phoenix myself but your version is way nicer

2 Likes

Very well done, @sevensidedmarble!
The demo page is a big plus, we need more of those in the LiveView ecosystem.

I will defo use this going forward as the default solution for Flash/Toast messages in Phoenix.

1 Like

Great lib! I’m trying to add icon to custom_toast_class_fn. Can you show some example?Tnanks!

1 Like

have you found a way to put an icon in a centrilized way?

I had to add an option to every put_toast I got in the code and it led me to encapsulate the put_toast or send_toast.

but if you want to know, that works:

#example

def my_icon(assigns) do
    ~H"""
    <.icon name="hero-exclamation-circle" class="mr-1" />
    """
  end

  @impl true
  def mount(%{"id" => id}, _session, socket) do
    LiveToast.put_toast(socket, :info, "Message",
      title: "Info",
      icon: &my_icon/1
    )
    ...
  end

but I wish I could centrilize it in, for example, <LiveToast.toast_group /> but it didn’t work.

if you find out a way please let me know

1 Like

No, unfortunately I haven’t found a way to use it, so i returned to good old put_flash

If you want to define a common function to use, you can, for instance do this fyi:

<LiveToast.toast_group
  flash={@flash}
  connected={assigns[:socket] != nil}
  group_class_fn={&CVBotWeb.Layouts.toast_group_class_fn/1}
/>

As for a default icon, I can probably just add a default_options or something like that.

Little belated but a couple weeks back I released a large new version, 0.7.0 to hex, which fixes some long standing issues with making the flash-fallback function properly across page navigations. have fun!

4 Likes

Question: Is it possible to configure it so that flash clearing is automatic after x milliseconds? So that I don’t need Process.send_after(self(), :clear_flash, x) somewhere.

I personally feel it’s a little out of scope, because the purpose of the library is that you would use the toasts as much as possible and just deal with the issues with flashes yourself (ie, if you want to add that send_after you can and should). But feel free to open a GH issue and we can discuss further and have others weigh in.

1 Like

Even better, I made a PR that does what I want. :cowboy_hat_face:

:thinking: not sure what could be some issues regarding this.

I also ran into that issue this morning and opened a ticket for it: Flashes don't disappear after push_navigate · Issue #43 · srcrip/live_toast · GitHub

For me it’s confusing to treat Flashes and Toasts differently although they look and feel exactly the same. Also, it means I can’t use this library because I have a lot of LiveViews that update a record and redirect back to the main view afterwards, which means I use Flashes more than Toasts.

1 Like

Wait, I’m also confused, what is the difference? :sweat_smile: I mean semantically, what’s a toast, what’s a flash?

If you use ‘put_toast/3’ but don’t navigate away, it’s a Toast. If you navigate away, it’s stored in the flash assign and becomes a Flash message. LiveView only uses Flash messages, LiveToast adds toasts to that which look and feel the same, but have slightly different behavior (e.g. auto-hiding). LiveToasts are assigned to the toasts_sync assign IIRC.

Edit: you can see how both are rendered

Here: live_toast/lib/live_toast/live_component.ex at master · srcrip/live_toast · GitHub

And here: live_toast/lib/live_toast/components.ex at master · srcrip/live_toast · GitHub

1 Like

I’m going to write some more in the documentation about this (which I know is lacking right now), but the way it works and the philosophy is basically this:

The default flash system has some quirks. The two big ones are:

  1. You can only ever have one flash of a given ‘kind’. You can’t have multiple info level events. You can only ever have one on screen at a time. This is the big problem. It makes the flash system basically incompatible with the UI people are used to where events can trigger multiple messages that push the last one up.

  2. You can’t really trigger them without having a reference to the socket. This isn’t usually a big deal but sometimes is slightly annoying in some async code.

This is why LiveToast creates a new system designed to replace the default flashes. It is not possible to solve the big issue number 1 without making something new.

However, I wanted it to feel like a drop in experience where you wouldn’t have to go and change all your put_flashs to put_toasts. So the library aims to make them look identical. But the idea would be you would go and change all those at some point because the toasts are strictly better.

Navigations

The early implementation of the library presented an issue with navigations however: if you used put_toast on the socket during a navigation, you wouldn’t get anything showing up after the navigation occurred. This was because the library stores state on the assigns of that socket (the one being navigated), and:

  1. Those assigns go away after the nav
  2. All events stop happening once the nav starts

These two things make it basically impossible to show a toast the way the library is designed across navigations. The only way we could do it would be to use some kind of extra sticky LV (which would suck to force people to have to do) or to store more state on the frontend, which would just add a huge complexity.

The simpler option was to force the library to just use flashes whenever we were in the middle of a navigation. Because they look the same, it should be a fairly seamless experience.

Like PJ said, originally flashes did not have any kind of auto-dismiss option, which was initially done on purpose because I kind of wanted to show that they were different, but that option has been added now (it’s called flash_duration).

4 Likes