Introduction

Rails Hotwire Turbo uses ActionCable to broadcast real-time updates via Turbo::StreamsChannel. When the channel is misconfigured, the broadcast silently fails, or the frontend does not receive updates, users see stale content without any error indication. This is especially problematic for notification systems, live dashboards, and collaborative features where real-time updates are core to the user experience.

Symptoms

  • Turbo stream broadcasts do not update the page
  • ActionCable logs show Turbo::StreamsChannel subscription errors
  • Database records update but UI does not reflect changes
  • turbo_stream_from helper renders but no WebSocket messages arrive
  • No errors in browser console or Rails logs

Check Turbo broadcast in Rails console: ``ruby # Test broadcast manually Turbo::StreamsChannel.broadcast_update_to( "posts", target: "post_123", partial: "posts/post", locals: { post: Post.find(123) } ) # Returns nil if successful, but page does not update

Common Causes

  • ActionCable not properly connected on the frontend
  • Turbo::StreamsChannel not subscribed in JavaScript
  • Broadcast to wrong stream name (singular vs plural)
  • Missing turbo-rails gem or incorrect version
  • Cable adapter misconfigured in production

Step-by-Step Fix

  1. 1.Verify ActionCable frontend connection:
  2. 2.```javascript
  3. 3.// app/javascript/controllers/application.js
  4. 4.import { Application } from "@hotwired/stimulus"
  5. 5.import { Turbo } from "@hotwired/turbo-rails"

// Ensure Turbo is connected to ActionCable // app/javascript/application.js import "@hotwired/turbo-rails" import "./controllers"

// app/javascript/channels/index.js // Load all channels from app/javascript/channels/ import { createConsumer } from "@rails/actioncable"

export default createConsumer() ```

  1. 1.Fix stream name matching in views and broadcasts:
  2. 2.```erb
  3. 3.<!-- WRONG - mismatched stream names -->
  4. 4.<!-- In view -->
  5. 5.<%= turbo_stream_from @user, "notifications" %>

<!-- In controller (wrong stream name) --> Turbo::StreamsChannel.broadcast_append_to( "user_notifications", # Does not match! target: "notifications", partial: "notifications/notification" )

<!-- CORRECT - matching stream names --> <!-- In view --> <%= turbo_stream_from @user, "notifications" %>

<!-- In controller --> Turbo::StreamsChannel.broadcast_append_to( [@user, "notifications"], # Matches turbo_stream_from target: "notifications", partial: "notifications/notification" ) ```

  1. 1.Verify Cable configuration in production:
  2. 2.```ruby
  3. 3.# config/cable.yml
  4. 4.production:
  5. 5.adapter: redis
  6. 6.url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
  7. 7.channel_prefix: myapp_production

# NOT this (in-memory adapter does not work across processes): # production: # adapter: async ```

  1. 1.**Debug Turbo stream broadcasts with logging":
  2. 2.```ruby
  3. 3.# config/initializers/turbo_logging.rb
  4. 4.module Turbo
  5. 5.module Broadcastable
  6. 6.extend ActiveSupport::Concern

included do after_commit -> { log_turbo_broadcast }, on: [:create, :update, :destroy] end

private

def log_turbo_broadcast Rails.logger.info( "[Turbo] Broadcasting for #{self.class}##{id}: " \ "action=#{persisted? ? 'update' : 'create'}" ) end end end ```

  1. 1.Test Turbo streams in development with Browser console:
  2. 2.```javascript
  3. 3.// In browser DevTools console
  4. 4.// Check ActionCable connection
  5. 5.Turbo.session.navigator.connection

// Check if subscribed to stream document.querySelector('[data-turbo-stream-source]')

// Manually trigger a turbo stream update Turbo.renderStreamMessage( <turbo-stream action="append" target="notifications"> <template><div>New notification</div></template> </turbo-stream> ) ```

Prevention

  • Always use model-based stream names (turbo_stream_from @post) rather than strings
  • Test real-time updates in integration tests with Turbo::StreamsChannel
  • Monitor ActionCable connection count in production metrics
  • Use turbo-rails gem version matching your Rails version
  • Add a heartbeat or connection status indicator to the UI
  • Document stream naming conventions for the team