LiveViewBus
A simple event bus for Phoenix LiveViews to send events between LiveViews and LiveComponents by ID—without manually wiring PubSub, topics, and handle_info every time.
Installation
Add live_view_bus to your list of dependencies in mix.exs:
def deps do
[
{:live_view_bus, "~> 0.1.0"}
]
endQuick start
1. Subscribe in mount
defmodule MyAppWeb.ProductLive.Index do
use MyAppWeb, :live_view
@impl true
def mount(_params, _session, socket) do
LiveViewBus.subscribe(MyApp.PubSub, "products_list")
{:ok, assign(socket, products: [], filters: %{})}
end
def handle_info({:live_view_bus, "products_list", "filters_changed", payload}, socket) do
products = load_products(payload)
{:noreply, assign(socket, products: products)}
end
end2. Send from another LiveView or Component
# When filters change in a sidebar component:
LiveViewBus.send(MyApp.PubSub, "products_list", "filters_changed", %{
category: "electronics",
sort: "price"
})That's it. No PubSub topic management, no boilerplate.
Usage
Using the on_mount hook
For automatic subscription when the LiveView mounts, use the on_mount hook in your router:
# In your router (lib/my_app_web/router.ex)
live_session :default, on_mount: [
{LiveViewBus.LiveView, :subscribe, pubsub: MyApp.PubSub, ids: ["products_list"]}
] do
live "/products", ProductLive.Index
endFor multiple IDs:
on_mount: [
{LiveViewBus.LiveView, :subscribe, pubsub: MyApp.PubSub, ids: ["products_list", "dashboard"]}
]Message format
All messages are delivered as:
{:live_view_bus, id, event, payload}id– the topic ID you subscribed toevent– the event name (string or atom)payload– the payload map (defaults to%{})
API reference
| Function | Description |
|---|---|
LiveViewBus.subscribe(pubsub, id) |
Subscribe the current process to id |
LiveViewBus.unsubscribe(pubsub, id) |
Unsubscribe from id |
LiveViewBus.send(pubsub, id, event, payload \\ %{}) |
Send event to all subscribers of id |
LiveViewBus.broadcast(pubsub, id, event, payload \\ %{}) |
Same as send/4 |
LiveViewBus.topic(id) | Returns the PubSub topic string (for advanced use) |
Note: Unsubscribe is optional. When the LiveView process ends, PubSub automatically removes the subscription.
Examples
Example 1: Filter sidebar + product list
Filter component (sends when user changes filters):
defmodule MyAppWeb.ProductLive.FilterComponent do
use MyAppWeb, :live_component
def handle_event("apply", %{"category" => category, "sort" => sort}, socket) do
LiveViewBus.send(MyApp.PubSub, "products_list", "filters_changed", %{
category: category,
sort: sort
})
{:noreply, socket}
end
endProduct list LiveView (receives and updates):
defmodule MyAppWeb.ProductLive.Index do
use MyAppWeb, :live_view
@impl true
def mount(_params, _session, socket) do
LiveViewBus.subscribe(MyApp.PubSub, "products_list")
{:ok, assign(socket, products: load_products(%{}))}
end
def handle_info({:live_view_bus, "products_list", "filters_changed", payload}, socket) do
products = load_products(payload)
{:noreply, assign(socket, products: products)}
end
defp load_products(filters) do
# Your Ecto query here
[]
end
endExample 2: Dashboard with multiple widgets
Widget A broadcasts when data is refreshed:
LiveViewBus.broadcast(MyApp.PubSub, "dashboard", "data_refreshed", %{
widget: "sales",
timestamp: DateTime.utc_now()
})Widget B and C subscribe and react:
def mount(_params, _session, socket) do
LiveViewBus.subscribe(MyApp.PubSub, "dashboard")
{:ok, socket}
end
def handle_info({:live_view_bus, "dashboard", "data_refreshed", payload}, socket) do
# Refresh this widget's data
{:noreply, assign(socket, data: fetch_data())}
endExample 3: Real-time room updates
# User joins room "room_123"
def mount(%{"room_id" => room_id}, _session, socket) do
LiveViewBus.subscribe(MyApp.PubSub, "room:#{room_id}")
{:ok, assign(socket, room_id: room_id)}
end
# Another user sends a message:
LiveViewBus.send(MyApp.PubSub, "room:#{room_id}", "new_message", %{
user: "alice",
body: "Hello!"
})
# All subscribers receive it:
def handle_info({:live_view_bus, _topic, "new_message", payload}, socket) do
{:noreply, assign(socket, messages: [payload | socket.assigns.messages])}
endLicense
MIT