Chat room
Action + live broadcast over Turbo Streams (same-process async cable in this demo).
ruby
Welcome to the phlex-reactive chat demo!
you
Send a message — it broadcasts with zero custom JS.
anon
Whatever
anon
Soemthign
anon
ff
anon
h;oi;
anon
test
anon
woohoo
anon
dsfds
anon
asdasd
anon
asd
anon
yo
anon
hey
anon
respond me
anon
test
anon
hello
anon
lol
anon
jh
anon
yo
anon
yo
anon
salut
anon
Il y a des Français ici ? :)
anon
oo
anon
ooo
anon
ùù
# The composer's send_message broadcasts the new message to every
# subscriber, excluding the sender's own connection:
render ChatRoomComponent.new(room: "lobby", messages: ChatMessage.for_room("lobby"))
# frozen_string_literal: true
# The chat room: subscribes to the room's Turbo Stream and renders the existing
# messages + the composer. New messages arrive via broadcast (see
# ChatComposerComponent#send_message), appended live to #chat-messages.
#
# NOTE: this demo runs on the in-process async Action Cable adapter, so live
# updates are same-process. True cross-tab/cross-client delivery needs a real
# pub/sub backend (Redis or pgbus) — out of scope for the demo site.
class ChatRoomComponent < Phlex::HTML
include Phlex::Rails::Helpers::TurboStreamFrom
def initialize(room: 'lobby', messages: [])
@room = room
@messages = messages
end
def view_template
div(class: 'flex flex-col gap-3') do
turbo_stream_from(*ChatMessage.stream_key(@room))
div(id: "chat-messages-#{@room}",
class: 'flex flex-col gap-1 min-h-32 max-h-80 overflow-y-auto',
data: { testid: 'chat-messages' }) do
@messages.each { render ChatMessageComponent.new(chat_message: it) }
end
render ChatComposerComponent.new(room: @room)
end
end
end