The ActionCable JavaScript client provides a complete browser-side API for establishing WebSocket connections, managing channel subscriptions, and handling real-time communication with ActionCable servers.
Main entry point for the ActionCable JavaScript client.
// Global ActionCable object
const ActionCable = {
// Internal configuration constants
INTERNAL: {
message_types: {
welcome: "welcome",
ping: "ping",
confirmation: "confirm_subscription",
rejection: "reject_subscription"
},
default_mount_path: "/cable",
protocols: ["actioncable-v1-json", "actioncable-unsupported"]
},
// WebSocket reference
WebSocket: window.WebSocket,
// Logger reference
logger: window.console,
// Create consumer connection
createConsumer(url?: string): Consumer;
// Get configuration from meta tags
getConfig(name: string): string | null;
// Convert HTTP URL to WebSocket URL
createWebSocketURL(url: string): string;
// Enable debug logging
startDebugging(): void;
// Disable debug logging
stopDebugging(): void;
// Log messages if debugging enabled
log(...messages: any[]): void;
}Usage Examples:
// Create consumer with default URL (from meta tag)
const consumer = ActionCable.createConsumer();
// Create consumer with specific URL
const consumer = ActionCable.createConsumer('ws://localhost:3000/cable');
// Enable debug logging
ActionCable.startDebugging();
// Get configuration from meta tags
const url = ActionCable.getConfig('url'); // Reads <meta name="action-cable-url">Establishes and manages the WebSocket connection to the ActionCable server.
class Consumer {
constructor(url: string);
// Connection management
connect(): void;
disconnect(): void;
// Send data through the connection
send(data: object): boolean;
// Ensure connection is active
ensureActiveConnection(): void;
// Subscription management
readonly subscriptions: Subscriptions;
// Connection instance
readonly connection: Connection;
// WebSocket URL
readonly url: string;
}Usage Examples:
// Create and connect consumer
const consumer = ActionCable.createConsumer('/cable');
// Send data (rarely used directly)
consumer.send({ command: 'subscribe', identifier: '{"channel":"ChatChannel"}' });
// Disconnect and reconnect
consumer.disconnect();
consumer.connect();
// Ensure active connection before operations
consumer.ensureActiveConnection();Internal WebSocket connection management (not typically used directly).
class Connection {
constructor(consumer: Consumer);
// Connection state
isOpen(): boolean;
isActive(): boolean;
getState(): string;
getProtocol(): string;
// Connection management
open(): boolean;
close(options?: { allowReconnect?: boolean }): void;
reopen(): void;
// Message transmission
send(data: object): boolean;
// Connection properties
readonly consumer: Consumer;
readonly subscriptions: Subscriptions;
readonly monitor: ConnectionMonitor;
readonly disconnected: boolean;
}Usage Examples:
const consumer = ActionCable.createConsumer();
const connection = consumer.connection;
// Check connection state
if (connection.isOpen()) {
console.log('Connection is open');
}
console.log('Current state:', connection.getState()); // 'connecting', 'open', 'closed'
console.log('Protocol:', connection.getProtocol()); // 'actioncable-v1-json'
// Force reconnection
connection.reopen();Monitors connection health with heartbeats and handles reconnection.
class ConnectionMonitor {
constructor(connection: Connection);
// Monitor control
start(): void;
stop(): void;
// Connection tracking
recordConnect(): void;
recordDisconnect(): void;
recordPing(): void;
// Monitor state
isRunning(): boolean;
// Properties
readonly connection: Connection;
}Manages channel subscriptions for a consumer.
class Subscriptions {
constructor(consumer: Consumer);
// Create subscription to a channel
create(channelName: string, mixin?: object): Subscription;
create(channelParams: object, mixin?: object): Subscription;
// Find subscriptions
findAll(identifier: string): Subscription[];
// Subscription lifecycle
reload(): void;
// Event notification
notify(identifier: string, callbackName: string, ...args: any[]): void;
notifyAll(callbackName: string, ...args: any[]): void;
// Subscription management
reject(identifier: string): void;
// Properties
readonly consumer: Consumer;
}Usage Examples:
const consumer = ActionCable.createConsumer();
// Subscribe to simple channel
const subscription = consumer.subscriptions.create('ChatChannel', {
received(data) {
console.log('Received:', data);
}
});
// Subscribe with parameters
const roomSubscription = consumer.subscriptions.create(
{ channel: 'ChatChannel', room_id: 123 },
{
connected() {
console.log('Connected to chat room');
},
disconnected() {
console.log('Disconnected from chat room');
},
received(data) {
console.log('Message:', data);
}
}
);
// Find all subscriptions for a channel
const chatSubs = consumer.subscriptions.findAll('{"channel":"ChatChannel","room_id":123}');Represents an individual channel subscription.
class Subscription {
constructor(consumer: Consumer, params: object, mixin?: object);
// Subscription actions
perform(action: string, data?: object): void;
send(data: object): boolean;
unsubscribe(): void;
// Callback methods (implement in mixin)
connected?(): void;
disconnected?(data?: { willAttemptReconnect: boolean }): void;
received?(data: any): void;
rejected?(): void;
// Properties
readonly consumer: Consumer;
readonly identifier: string;
}Usage Examples:
// Create subscription with callbacks
const subscription = consumer.subscriptions.create('ChatChannel', {
// Called when subscription is confirmed by server
connected() {
console.log('Successfully subscribed to ChatChannel');
},
// Called when connection is lost
disconnected(data) {
console.log('Disconnected. Will reconnect:', data.willAttemptReconnect);
},
// Called when data is received from the channel
received(data) {
console.log('Received message:', data.message);
document.getElementById('messages').innerHTML +=
`<div>${data.user}: ${data.message}</div>`;
},
// Called if subscription is rejected by server
rejected() {
console.log('Subscription was rejected');
}
});
// Send action to channel
subscription.perform('speak', { message: 'Hello everyone!' });
// Unsubscribe when done
subscription.unsubscribe();const consumer = ActionCable.createConsumer();
const rooms = {};
function joinRoom(roomId) {
if (rooms[roomId]) return rooms[roomId];
rooms[roomId] = consumer.subscriptions.create(
{ channel: 'ChatChannel', room_id: roomId },
{
connected() {
console.log(`Joined room ${roomId}`);
},
received(data) {
displayMessage(roomId, data);
},
speak(message) {
this.perform('speak', { message, room_id: roomId });
}
}
);
return rooms[roomId];
}
function leaveRoom(roomId) {
if (rooms[roomId]) {
rooms[roomId].unsubscribe();
delete rooms[roomId];
}
}
// Usage
const room1 = joinRoom(1);
const room2 = joinRoom(2);
room1.speak('Hello room 1!');
room2.speak('Hello room 2!');const consumer = ActionCable.createConsumer();
// Monitor connection state
consumer.connection.monitor.start();
// Handle connection events
const originalOpen = consumer.connection.events.open;
consumer.connection.events.open = function() {
console.log('Connection opened');
document.body.classList.add('connected');
originalOpen.call(this);
};
const originalClose = consumer.connection.events.close;
consumer.connection.events.close = function(event) {
console.log('Connection closed');
document.body.classList.remove('connected');
originalClose.call(this, event);
};// Consumer with authentication token
const consumer = ActionCable.createConsumer('/cable?token=' + authToken);
// Or using query parameters in subscription
const subscription = consumer.subscriptions.create(
{
channel: 'PrivateChannel',
token: userToken,
room_id: roomId
},
{
connected() {
console.log('Authenticated and connected');
},
rejected() {
console.log('Authentication failed');
// Redirect to login or show error
}
}
);const consumer = ActionCable.createConsumer();
// Handle connection errors
consumer.connection.events.error = function() {
console.log('WebSocket error occurred');
showConnectionError();
};
// Custom reconnection logic
const originalReopen = consumer.connection.reopen;
consumer.connection.reopen = function() {
console.log('Attempting to reconnect...');
showReconnectingMessage();
originalReopen.call(this);
};
// Monitor connection health
setInterval(() => {
if (!consumer.connection.isActive()) {
console.log('Connection lost, attempting to reconnect');
consumer.connection.reopen();
}
}, 5000);// app/assets/javascripts/cable.js
//= require action_cable
//= require_self
//= require_tree ./channels
(function() {
this.App || (this.App = {});
App.cable = ActionCable.createConsumer();
}).call(this);// app/assets/javascripts/channels/chat.js
App.chatChannel = App.cable.subscriptions.create('ChatChannel', {
connected: function() {
console.log('Connected to ChatChannel');
},
disconnected: function() {
console.log('Disconnected from ChatChannel');
},
received: function(data) {
console.log('Received data:', data);
},
speak: function(message) {
return this.perform('speak', { message: message });
}
});<!-- In Rails layout -->
<%= action_cable_meta_tag %>
<!-- Generates: <meta name="action-cable-url" content="/cable" /> -->