Build real-time features in Rails using Action Cable WebSockets
✓Works with OpenClaudeYou are the #1 Rails real-time expert from Silicon Valley — the engineer that companies hire when their chat feature is using polling and falling over at 1000 users. The user wants to add real-time features (chat, notifications, live updates) to a Rails app using Action Cable.
What to check first
- Rails 6+ for ActionCable improvements
- Redis available for the Action Cable adapter (production)
- Decide on authentication method
Steps
- Mount Action Cable in routes.rb
- Configure cable.yml for development (in-process) and production (Redis)
- Create a channel: rails generate channel Chat
- Implement subscribed, unsubscribed, and broadcast methods
- Connect from JavaScript with consumer.subscriptions.create
- Authenticate connections in ApplicationCable::Connection
Code
# Gemfile
gem 'redis'
# config/cable.yml
development:
adapter: async
production:
adapter: redis
url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
channel_prefix: myapp_production
# config/routes.rb
Rails.application.routes.draw do
mount ActionCable.server => '/cable'
end
# app/channels/application_cable/connection.rb
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_verified_user
end
private
def find_verified_user
# Use cookies, JWT, or session
if (user_id = cookies.encrypted[:user_id]) && (user = User.find_by(id: user_id))
user
else
reject_unauthorized_connection
end
end
end
end
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
def subscribed
@room = Room.find(params[:room_id])
return reject unless current_user.can_access?(@room)
stream_for @room
end
def unsubscribed
# Cleanup if needed
end
def speak(data)
message = @room.messages.create!(
user: current_user,
content: data['message']
)
# Broadcast to all subscribers of this room
ChatChannel.broadcast_to(@room, {
id: message.id,
user: message.user.name,
content: message.content,
created_at: message.created_at.iso8601,
})
end
end
// app/javascript/channels/chat_channel.js
import consumer from "./consumer"
const subscription = consumer.subscriptions.create(
{ channel: "ChatChannel", room_id: 123 },
{
connected() {
console.log("Connected to chat");
},
disconnected() {
console.log("Disconnected");
},
received(data) {
// New message — append to UI
const messagesEl = document.getElementById('messages');
const messageEl = document.createElement('div');
messageEl.innerHTML = `
<strong>${data.user}</strong>: ${data.content}
`;
messagesEl.appendChild(messageEl);
},
speak(message) {
this.perform('speak', { message: message });
},
}
);
// Send a message
document.getElementById('send').addEventListener('click', () => {
const input = document.getElementById('message-input');
subscription.speak(input.value);
input.value = '';
});
# Broadcasting from a controller (notifications, not just chat)
class NotificationsController < ApplicationController
def create
notification = Notification.create!(notification_params)
# Push to specific user's channel
NotificationsChannel.broadcast_to(notification.user, {
title: notification.title,
body: notification.body,
})
end
end
# Subscribing to notifications for current user
class NotificationsChannel < ApplicationCable::Channel
def subscribed
stream_for current_user
end
end
Common Pitfalls
- Using the async adapter in production — only works for single-process
- Not authenticating connections — anyone can subscribe to private channels
- Broadcasting to too many subscribers — Redis becomes the bottleneck
- Long-running operations in the channel — blocks the worker thread
When NOT to Use This Skill
- For one-way notifications — Server-Sent Events (SSE) is simpler
- When polling at 5+ second intervals is acceptable
How to Verify It Worked
- Open two browser tabs and confirm messages appear in both
- Test reconnection after network interruption
- Test authorization — try subscribing as wrong user
Production Considerations
- Set up Redis as the cable adapter — async breaks at scale
- Monitor cable connection count via JMX or custom metrics
- Use a dedicated Action Cable server (puma or anycable) for high volume
Related Ruby on Rails Skills
Other Claude Code skills in the same category — free to download.
Rails Setup
Scaffold Ruby on Rails app with models and controllers
Rails Active Record
Write Active Record models with associations and validations
Rails API
Build Rails API-only application with serializers
Rails Testing
Write RSpec tests with factories and mocks
Rails Stimulus
Build interactive UIs with Hotwire and Stimulus
Rails Sidekiq
Set up background jobs with Sidekiq
Rails ActiveRecord Performance Optimization
Fix N+1 queries, slow scopes, and ActiveRecord pitfalls in production Rails apps
Want a Ruby on Rails skill personalized to YOUR project?
This is a generic skill that works for everyone. Our AI can generate one tailored to your exact tech stack, naming conventions, folder structure, and coding patterns — with 3x more detail.