ably-realtime
Ably real-time messaging patterns, WebSocket channel management, message validation and processing, staleness filtering, error recovery strategies, collaborative editing with drag-and-drop, optimistic updates for voting, real-time board collaboration, and Ably integration best practices for ree-board project
Best use case
ably-realtime is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Ably real-time messaging patterns, WebSocket channel management, message validation and processing, staleness filtering, error recovery strategies, collaborative editing with drag-and-drop, optimistic updates for voting, real-time board collaboration, and Ably integration best practices for ree-board project
Teams using ably-realtime should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/ably-realtime/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How ably-realtime Compares
| Feature / Agent | ably-realtime | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Ably real-time messaging patterns, WebSocket channel management, message validation and processing, staleness filtering, error recovery strategies, collaborative editing with drag-and-drop, optimistic updates for voting, real-time board collaboration, and Ably integration best practices for ree-board project
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Ably Real-time Collaboration
## When to Use This Skill
Activate this skill when working on:
- Implementing real-time features
- Setting up Ably channels
- Processing real-time messages
- Building collaborative editing features
- Implementing drag-and-drop with real-time sync
- Handling WebSocket connections
- Managing real-time state updates
- Optimizing real-time performance
## Core Patterns
### Channel Management
**Channel Naming Convention:**
```typescript
// Pattern: `board:{boardId}`
const channelName = `board:${boardId}`;
```
**Setting Up Channels:**
```typescript
"use client";
import { useChannel } from "ably/react";
import { useEffect } from "react";
export function PostChannelComponent({ boardId }: { boardId: string }) {
const { channel } = useChannel(`board:${boardId}`, (message) => {
processMessage(message);
});
return <PostList boardId={boardId} />;
}
```
### Message Processors with Validation
**Critical Pattern:** Always validate messages before processing
```typescript
// lib/realtime/messageProcessors.ts
import { z } from "zod";
// Define message schema
const PostUpdateMessageSchema = z.object({
type: z.literal("post:update"),
postId: z.string(),
content: z.string().min(1).max(1000),
userId: z.string(),
timestamp: z.number(),
});
// Message processor
export const processPostUpdate = (rawData: unknown) => {
try {
// ✅ Validate message structure
const data = PostUpdateMessageSchema.parse(rawData);
// ✅ Check staleness (30s threshold)
const now = Date.now();
const age = now - data.timestamp;
if (age > 30000) {
console.warn("Stale message discarded", {
type: data.type,
age,
postId: data.postId,
});
return;
}
// ✅ Process validated, fresh message
updatePostContent(data.postId, data.content);
} catch (error) {
if (error instanceof z.ZodError) {
console.error("Invalid message structure", {
details: error.errors,
rawData,
});
} else {
console.error("Message processing error", error);
}
}
};
```
### Staleness Filtering
**30-Second Threshold:** Prevents processing old messages after reconnection
```typescript
const STALENESS_THRESHOLD_MS = 30000;
export function isMessageStale(timestamp: number): boolean {
const age = Date.now() - timestamp;
return age > STALENESS_THRESHOLD_MS;
}
// Usage in message handler
useChannel(`board:${boardId}`, (message) => {
const { timestamp } = message.data;
if (isMessageStale(timestamp)) {
console.warn("Dropping stale message", { age: Date.now() - timestamp });
return;
}
processMessage(message.data);
});
```
### Error Recovery Strategies
**Connection Error Handling:**
```typescript
import { useConnectionStateListener } from "ably/react";
export function RealtimeProvider({ children }: { children: React.ReactNode }) {
const [connectionState, setConnectionState] = useState<string>("initialized");
useConnectionStateListener((stateChange) => {
setConnectionState(stateChange.current);
switch (stateChange.current) {
case "connected":
console.log("✅ Connected to Ably");
break;
case "disconnected":
console.warn("⚠️ Disconnected from Ably");
break;
case "suspended":
console.error("❌ Connection suspended");
// Optionally show user notification
break;
case "failed":
console.error("❌ Connection failed");
// Show error message to user
break;
}
});
return (
<>
{connectionState !== "connected" && (
<ConnectionBanner state={connectionState} />
)}
{children}
</>
);
}
```
### Publishing Messages
**Always Include Timestamp:**
```typescript
import { useChannel } from "ably/react";
export function usePublishPostUpdate() {
const { channel } = useChannel(`board:${boardId}`);
const publishUpdate = async (postId: string, content: string) => {
await channel.publish("post:update", {
type: "post:update",
postId,
content,
userId: currentUserId,
timestamp: Date.now(), // ✅ Always include timestamp
});
};
return publishUpdate;
}
```
### Optimistic Updates for Voting
**Pattern:** Update UI immediately, sync in background
```typescript
"use client";
import { voteSignal } from "@/lib/signal/postSignals";
import { useChannel } from "ably/react";
export function VoteButton({
postId,
boardId,
}: {
postId: string;
boardId: string;
}) {
const { channel } = useChannel(`board:${boardId}`);
const handleVote = async () => {
// ✅ Optimistic update (immediate UI feedback)
voteSignal.value = {
...voteSignal.value,
[postId]: (voteSignal.value[postId] || 0) + 1,
};
try {
// Persist to database
await submitVote(postId);
// Broadcast to other users
await channel.publish("post:vote", {
type: "post:vote",
postId,
increment: 1,
timestamp: Date.now(),
});
} catch (error) {
// ❌ Rollback on error
voteSignal.value = {
...voteSignal.value,
[postId]: voteSignal.value[postId] - 1,
};
console.error("Vote failed", error);
}
};
return <button onClick={handleVote}>Vote</button>;
}
```
### Drag-and-Drop Integration
**Lazy-Loaded with Real-Time Sync:**
```typescript
// components/board/PostProvider.tsx
"use client";
import { useChannel } from "ably/react";
import dynamic from "next/dynamic";
// ✅ Lazy load drag-and-drop (reduces initial bundle)
const DragDropArea = dynamic(() => import("./DragDropArea"), { ssr: false });
export function PostProvider({ boardId }: { boardId: string }) {
useChannel(`board:${boardId}`, (message) => {
if (message.name === "post:move") {
handlePostMove(message.data);
}
});
const handleDrop = async (postId: string, newType: PostType) => {
// Update locally
movePostSignal(postId, newType);
// Persist to database
await updatePostType(postId, newType);
// Broadcast to other users
channel.publish("post:move", {
type: "post:move",
postId,
newType,
timestamp: Date.now(),
});
};
return <DragDropArea onDrop={handleDrop} />;
}
```
## Anti-Patterns
### ❌ Not Validating Messages
**Bad:**
```typescript
useChannel(`board:${boardId}`, (message) => {
// ❌ Trusts message data completely
updatePost(message.data.postId, message.data.content);
});
```
**Good:**
```typescript
useChannel(`board:${boardId}`, (message) => {
// ✅ Validates before processing
const validated = PostUpdateSchema.safeParse(message.data);
if (!validated.success) return;
updatePost(validated.data.postId, validated.data.content);
});
```
### ❌ Not Checking Message Staleness
**Bad:**
```typescript
useChannel(channelName, (message) => {
// ❌ Processes all messages, even old ones after reconnect
processMessage(message.data);
});
```
**Good:**
```typescript
useChannel(channelName, (message) => {
// ✅ Filters stale messages
if (isMessageStale(message.data.timestamp)) return;
processMessage(message.data);
});
```
### ❌ Not Handling Connection Errors
**Bad:**
```typescript
// ❌ No error handling
const { channel } = useChannel(channelName);
```
**Good:**
```typescript
// ✅ Monitor connection state
useConnectionStateListener((stateChange) => {
if (stateChange.current === "failed") {
showErrorNotification("Real-time connection lost");
}
});
```
### ❌ Publishing Without Timestamp
**Bad:**
```typescript
channel.publish("update", {
postId,
content,
// ❌ No timestamp for staleness check
});
```
**Good:**
```typescript
channel.publish("update", {
postId,
content,
timestamp: Date.now(), // ✅ Include timestamp
});
```
### ❌ Not Handling Race Conditions
**Bad:**
```typescript
// ❌ Multiple updates could conflict
const handleVote = async () => {
const newCount = currentCount + 1;
await updateVoteCount(postId, newCount);
};
```
**Good:**
```typescript
// ✅ Use atomic increment
const handleVote = async () => {
await db
.update(postTable)
.set({ voteCount: sql`vote_count + 1` })
.where(eq(postTable.id, postId));
};
```
## Integration with Other Skills
- **[rbac-security](../rbac-security/SKILL.md):** Validate messages for security
- **[signal-state-management](../signal-state-management/SKILL.md):** Real-time updates to signals
- **[nextjs-app-router](../nextjs-app-router/SKILL.md):** Client components for real-time features
- **[testing-patterns](../testing-patterns/SKILL.md):** Test message processors with fake timers
## Project-Specific Context
### Key Files
- `lib/realtime/messageProcessors.ts` - Message validation and processing
- `components/board/PostProvider.tsx` - Real-time channel setup
- `components/board/PostChannelComponent.tsx` - Channel subscription
- `lib/realtime/__tests__/` - Message processor tests
### Message Types
**Current Message Types:**
```typescript
type MessageType =
| "post:create"
| "post:update"
| "post:delete"
| "post:move"
| "post:vote"
| "member:join"
| "member:leave";
```
### Channel Structure
**One Channel Per Board:**
- Channel name: `board:{boardId}`
- All board events published to this channel
- Subscribers filter by message type
### Performance Optimizations
1. **Lazy Loading:** Drag-and-drop loaded on demand
2. **Staleness Filter:** Discards messages >30s old
3. **Optimistic Updates:** Immediate UI feedback
4. **Batching:** Vote counts updated atomically
5. **Connection Pooling:** Reuse Ably client instance
### Error Recovery
**Automatic Reconnection:**
- Ably SDK handles reconnection automatically
- Messages buffered during disconnection
- Staleness filter prevents processing old messages after reconnect
**Manual Recovery:**
```typescript
// Refresh data after long disconnection
if (
stateChange.previous === "suspended" &&
stateChange.current === "connected"
) {
await refreshBoardData();
}
```
### Testing
**Mock Ably in Tests:**
```typescript
jest.mock("ably/react", () => ({
useChannel: jest.fn(() => ({
channel: {
publish: jest.fn(),
subscribe: jest.fn(),
},
})),
}));
```
---
**Last Updated:** 2026-01-10Related Skills
action-cable-realtime
This skill should be used when the user asks about Action Cable, WebSockets, real-time features, channels, broadcasting, subscriptions, chat applications, live notifications, presence indicators, collaborative editing, server push, pub/sub patterns, Solid Cable, or streaming updates. Also use when discussing real-time architecture, WebSocket deployment, or alternatives like polling and Server-Sent Events. Examples:
ably
Implements real-time pub/sub messaging with Ably's edge infrastructure. Use when building real-time features requiring enterprise reliability, presence, message history, and global low-latency delivery.
ontopo
An AI agent skill to search for Israeli restaurants, check table availability, view menus, and retrieve booking links via the Ontopo platform, acting as an unofficial interface to its data.
grail-miner
This skill assists in setting up, managing, and optimizing Grail miners on Bittensor Subnet 81, handling tasks like environment configuration, R2 storage, model checkpoint management, and performance tuning.
chrome-debug
This skill empowers AI agents to debug web applications and inspect browser behavior using the Chrome DevTools Protocol (CDP), offering both collaborative (headful) and automated (headless) modes.
whisper-transcribe
Transcribes audio and video files to text using OpenAI's Whisper CLI, enhanced with contextual grounding from local markdown files for improved accuracy.
ux
This AI agent skill provides comprehensive guidance for creating professional and insightful User Experience (UX) designs, covering user research, information architecture, interaction design, visual guidance, and usability evaluation. It aims to produce actionable, user-centered solutions that avoid generic AI aesthetics.
thor-skills
An entry point and router for AI agents to manage various THOR-related cybersecurity tasks, including running scans, analyzing logs, troubleshooting, and maintenance.
lets-go-rss
A lightweight, full-platform RSS subscription manager that aggregates content from YouTube, Vimeo, Behance, Twitter/X, and Chinese platforms like Bilibili, Weibo, and Douyin, featuring deduplication and AI smart classification.
tech-blog
Generates comprehensive technical blog posts, offering detailed explanations of system internals, architecture, and implementation, either through source code analysis or document-driven research.
vly-money
Generate crypto payment links for supported tokens and networks, manage access to X402 payment-protected content, and provide direct access to the vly.money wallet interface.
modal-deployment
Run Python code in the cloud with serverless containers, GPUs, and autoscaling using Modal. This skill enables agents to generate code for deploying ML models, running batch jobs, serving APIs, and scaling compute-intensive workloads.