PubSub.broadcast inside live component ignores topic, calls parent?


I am working on building out a simple chat interface using LiveView and live components. My approach was to have each LC use Phoenix.PubSub.subscribe/broadcast, as I was aiming for a simple drop-in component which didn’t require altering the parent LV state/etc.

However, I’ve found that calling Phoenix.PubSub.broadcast/3 from inside the LC seems to ignore the broadcast topic, triggering an event on the parent LV instead.

Here’s a (brief) code snippet to demonstrate:

# my_chat_component.ex
def mount(socket) do
  Phoenix.PubSub.subscribe(Example.PubSub, "chat:id")
def handle_event("send_message", message, socket) do
  Phoenix.PubSub.broadcast(Example.PubSub, "chat:id", {:chat_message, message})
def handle_info({:chat_message, data}, socket) do
  # This does not fire!

In this code, I’d expect that any listener to topic chat:id would be alerted with the :chat_message data, and the local handle_info would be called. However, when broadcast/3 is called, the local handle_info is not fired, and an error raises from the parent (who is not even subscribed to the topic at all):

function ExampleWeb.ParentView.handle_info/2 is undefined or private

I’m pretty new to Phoenix/Elixir in general, so perhaps I am just using the wrong broadcast or have the wrong mental model around how LC’s function as sub-processes to their LV parent? Maybe this is an actual bug in LV? I’m not sure. For what it’s worth, I am using PubSub features in other pieces of my application and it seems to work as expected, so this seems to be an LV<->LC issue.

Any help would be greatly appreciated. Thank you!


have the wrong mental model around how LC’s function as sub-processes to their LV parent?

Live components are not processes hence they can’t have handle_info callbacks since all messages would be received by their “parent” live view anyway.

Any calls to Phoenix.PubSub.subscribe(Example.PubSub, "chat:id") also subscribe the current process (self/0) by default which is the “parent” live view, not the live component.

From the second paragraph in

Components run inside the LiveView process, but may have their own state and event handling.

1 Like

Thank you for the remarkably quick response! I am disappointed to hear that my PubSub idea doesn’t work within LC’s, but your explanation makes a lot of sense as to why it was breaking.

At this point, I’m assuming my best path forward is to make a macro which defines the handle_event code and drop it into parents that use the chat component. I think that’d keep me on track for being able to drop-in the functionality while modifying parents as little as possible (but feel free to correct me if I’m wrong!).

Thank you again for your help! I was really scratching my head over this.

Or maybe you can replace live components with live views? Then PubSub would work as expected.

I had a similar (I think) problem with a dashboard consisting of widgets where dashboard was a live view and each widget was a live component.

At first it was working fine but then I decided that I want to fetch the data for each widget asynchronously. I tried calling Task.async in the widget component, then received the response in the dashboard live view’s handle_info and called send_update there as well to update the widget component (they were “stateful” since they also needed to be able to handle some user interactions).

It was a bit complicated, so eventually I just went with making each widget a live view.

1 Like