Example: notifications / live badges (pure broadcast, no client action)

Not every reactive component needs a client action. Sometimes the server just needs to push a re-render — a notification badge that updates when a job finishes, a live metric, a “new items available” pill. This uses only Phlex::Reactive::Streamable (broadcasts), no Component mixin.

A notification badge

class NotificationsBadge < ApplicationComponent
  include Phlex::Reactive::Streamable

  def initialize(user:) = @user = user
  def id = dom_id(@user, :notifications_badge)
  def self.model_param_name = :user

  def view_template
    span(id:, class: "badge") do
      count = @user.notifications.unread.count
      plain(count.zero? ? "" : count.to_s)
    end
  end
end

Subscribe on the page:

# in a layout or header component
turbo_stream_from current_user, :notifications

Push an update from anywhere — a model callback, a job, a service:

class Notification < ApplicationRecord
  belongs_to :user
  after_create_commit do
    NotificationsBadge.broadcast_replace_to(user, :notifications, model: user)
  end
end

Now creating a notification re-renders the badge in every tab the user has open. No client action, no Stimulus, no Action Cable.

A “new items” pill driven by a background job

class ImportJob < ApplicationJob
  def perform(import)
    import.run!
    # Re-render a status component for everyone watching this import.
    Imports::Status.broadcast_replace_to(import, model: import)
  end
end
class Imports::Status < ApplicationComponent
  include Phlex::Reactive::Streamable

  def initialize(import:) = @import = import
  def id = dom_id(@import, :status)

  def view_template
    div(id:, class: "import-status #{@import.status}") do
      plain @import.status.titleize
      plain " — #{@import.processed_count}/#{@import.total_count}" if @import.running?
    end
  end
end

When to use this vs a full reactive component

Need Use
Server pushes a re-render (job, callback, another user) Streamable + broadcast_*_to
User clicks/types and the same component updates add Component + action
Both add Component; broadcast from inside the action

Because both paths target the component by its id, you can start with a pure broadcast badge and later add a dismiss action without changing the target or the subscription.

Transactional safety

If you broadcast from an after_create_commit/after_update_commit callback (or from inside a transaction with pgbus), the broadcast only fires once the change commits — a rolled-back notification never flashes a phantom badge. See docs/broadcasting.md.


This site uses Just the Docs, a documentation theme for Jekyll.