View Source Live Toast

CI Hex

Live Toast is a drop-in replacement for the flash system in Phoenix/LiveView.

Installation

Add live_toast to your list of dependencies in the mix.exs of your Phoenix application:

def deps do
  [
    {:live_toast, "~> 0.3.0"}
  ]
end

Next open up your app.js and import/setup the hook:

import { createLiveToastHook } from 'live_toast'

let liveSocket = new LiveSocket('/live', Socket, {
  hooks: {
    LiveToast: createLiveToastHook()
  }
})

Then, add '../deps/live_toast/lib/**/*.*ex' to your list of paths Tailwind will look for class names, in your tailwind.config.js:

// assets/tailwind.config.js

module.exports = {
  content: [
    './js/**/*.js',
    '../lib/your_app_web.ex',
    '../lib/your_app_web/**/*.*ex',
    '../deps/live_toast/lib/**/*.*ex',
  ]
}

Your particular file will look different but all you need to do is make sure the last line is there.

Note that the classes are currently hardcoded. Configuration of the toast components, and therefore there styling, are on the roadmap. But the default styles should look pretty good in the mean time.

Finally, replace your <.flash_group /> component with the new <LiveToast.toast_group />. It's most likely in your app.html.heex:

<!-- Remove this! -->
<.flash_group flash={@flash} />

<!-- And replace it with this: -->
<LiveToast.toast_group flash={@flash} connected={assigns[:socket] != nil} />

<%= @inner_content %>

And you're done! Note that it's very important to set connected based on whether we're in a LiveView or not. This controls toast/flash display on non-LiveView pages.

Usage

LiveToast will hijack the usual display of your flash messages, so they will continue to work as normal. You can continue to use flashes as normal, if you want to.

However, one of the reasons to not use flash messages, is the Phoenix flash system only allows one message for each kind of flash. The toast pattern, alternatively, generally allows for multiple messages displayed to the user at at time.

From a LiveView, you can now use send_toast:

defmodule YourApp.SomeLiveView do
  def handle_event("submit", _payload, socket) do
  # you do some thing with the payload, then you want to show a toast, so:
  LiveToast.send_toast(:info, "Upload successful.")

  {:noreply, socket}
  end
end

Or you can use the helper function, put_toast, similar to how you may use put_flash:

defmodule YourApp.SomeLiveView do
  def handle_event("submit", _payload, socket) do
    socket = socket
    |> put_toast(:info, "Upload successful.")

    {:noreply, socket}
  end
end

put_toast can take a Phoenix.LiveView.Socket or a Plug.Conn, so you can use the same thing in your live and non-live pages.

defmodule YourApp.SomeController do
  def create(conn, _params) do
    conn
    |> put_toast(:info, "Upload successful.")
    |> render(:whatever)
  end
end

Configuration

You can define a custom toast class function, like so:

defmodule MyModule do
  def toast_class_fn(assigns) do
    [
      "w-80 sm:w-96 z-50 p-2 rounded-md shadow origin-center overflow-hidden",
      assigns[:rest][:hidden] != true && "flex",
      assigns[:kind] == :info && "text-gray-800 bg-gray-50 dark:bg-gray-800 dark:text-gray-300",
      assigns[:kind] == :success && "text-green-800 bg-green-50 dark:bg-gray-800 dark:text-green-400",
      assigns[:kind] == :error && "text-red-800 bg-red-50 dark:bg-gray-800 dark:text-red-400"
    ]
  end
end

And then use it to override the default styles:

<LiveToast.toast_group flash={@flash} connected={assigns[:socket] != nil} toast_class_fn={MyModule.toast_class_fn/1} />

And that's pretty much it.

Roadmap

Some of the stuff still to work on:

  • [ ] Improved docs
  • [ ] Configuration for the classes on toasts
  • [ ] more tests
  • [ ] More configuration for the animations
  • [ ] Ability to have more flashes than the default :error and :info (like a :warn)
  • [ ] Update a toast live (showing progress for example), and a recipe entry on this