> pusher
You are an expert in Pusher Channels, the hosted real-time messaging platform. You help developers add live features to applications using pub/sub channels, presence channels for online status, private channels with auth, client events for peer-to-peer, webhooks, and batch triggers — enabling real-time notifications, live dashboards, chat, and collaborative features without managing WebSocket infrastructure.
curl "https://skillshub.wtf/TerminalSkills/skills/pusher?format=md"Pusher — Real-Time Communication Platform
You are an expert in Pusher Channels, the hosted real-time messaging platform. You help developers add live features to applications using pub/sub channels, presence channels for online status, private channels with auth, client events for peer-to-peer, webhooks, and batch triggers — enabling real-time notifications, live dashboards, chat, and collaborative features without managing WebSocket infrastructure.
Core Capabilities
Server-Side (Node.js)
import Pusher from "pusher";
const pusher = new Pusher({
appId: process.env.PUSHER_APP_ID!,
key: process.env.PUSHER_KEY!,
secret: process.env.PUSHER_SECRET!,
cluster: "us2",
useTLS: true,
});
// Trigger event on a channel
await pusher.trigger("orders", "new-order", {
id: "ord-123",
total: 99.99,
customer: "Alice",
});
// Batch trigger (multiple channels)
await pusher.triggerBatch([
{ channel: "user-42", name: "notification", data: { message: "New message" } },
{ channel: "dashboard", name: "metric-update", data: { activeUsers: 1234 } },
]);
// Private channel auth (Express middleware)
app.post("/pusher/auth", (req, res) => {
const socketId = req.body.socket_id;
const channel = req.body.channel_name;
// Verify user has access to this channel
if (!userCanAccessChannel(req.user, channel)) {
return res.status(403).json({ error: "Forbidden" });
}
const auth = pusher.authorizeChannel(socketId, channel);
res.json(auth);
});
// Presence channel auth (includes user info)
app.post("/pusher/auth", (req, res) => {
const socketId = req.body.socket_id;
const channel = req.body.channel_name;
if (channel.startsWith("presence-")) {
const auth = pusher.authorizeChannel(socketId, channel, {
user_id: req.user.id,
user_info: { name: req.user.name, avatar: req.user.avatar },
});
return res.json(auth);
}
const auth = pusher.authorizeChannel(socketId, channel);
res.json(auth);
});
Client-Side (React)
import Pusher from "pusher-js";
import { useEffect, useState } from "react";
const pusher = new Pusher(process.env.NEXT_PUBLIC_PUSHER_KEY!, {
cluster: "us2",
authEndpoint: "/api/pusher/auth",
});
function useChannel<T>(channelName: string, eventName: string): T | null {
const [data, setData] = useState<T | null>(null);
useEffect(() => {
const channel = pusher.subscribe(channelName);
channel.bind(eventName, (payload: T) => setData(payload));
return () => { pusher.unsubscribe(channelName); };
}, [channelName, eventName]);
return data;
}
function LiveDashboard() {
const metric = useChannel<{ activeUsers: number }>("dashboard", "metric-update");
return <div>Active Users: {metric?.activeUsers ?? "..."}</div>;
}
// Presence channel (who's online)
function OnlineUsers({ roomId }: { roomId: string }) {
const [members, setMembers] = useState<any[]>([]);
useEffect(() => {
const channel = pusher.subscribe(`presence-room-${roomId}`);
channel.bind("pusher:subscription_succeeded", (m: any) => {
setMembers(Object.values(m.members));
});
channel.bind("pusher:member_added", (m: any) => {
setMembers(prev => [...prev, m.info]);
});
channel.bind("pusher:member_removed", (m: any) => {
setMembers(prev => prev.filter(p => p.id !== m.id));
});
return () => { pusher.unsubscribe(`presence-room-${roomId}`); };
}, [roomId]);
return <ul>{members.map(m => <li key={m.id}>{m.name}</li>)}</ul>;
}
Installation
npm install pusher # Server
npm install pusher-js # Client
Best Practices
- Channel naming — Public:
orders; Private:private-user-42; Presence:presence-room-5 - Private channels — Use for user-specific data; server must authorize via auth endpoint
- Presence for online status — Use presence channels to track who's online; includes user info
- Batch triggers — Use
triggerBatch()for multiple events; single API call, reduced latency - Client events — Enable for peer-to-peer (typing indicators, cursor position); no server roundtrip
- Webhooks — Configure webhooks for channel_occupied/vacated events; track active channels server-side
- Max payload 10KB — Keep event data small; send IDs and fetch details client-side if needed
- Fallback — Pusher auto-falls back from WebSocket to HTTP streaming/polling for restrictive networks
> related_skills --same-repo
> zustand
You are an expert in Zustand, the small, fast, and scalable state management library for React. You help developers manage global state without boilerplate using Zustand's hook-based stores, selectors for performance, middleware (persist, devtools, immer), computed values, and async actions — replacing Redux complexity with a simple, un-opinionated API in under 1KB.
> zod
You are an expert in Zod, the TypeScript-first schema declaration and validation library. You help developers define schemas that validate data at runtime AND infer TypeScript types at compile time — eliminating the need to write types and validators separately. Used for API input validation, form validation, environment variables, config files, and any data boundary.
> xero-accounting
Integrate with the Xero accounting API to sync invoices, expenses, bank transactions, and contacts — and generate financial reports like P&L and balance sheet. Use when: connecting apps to Xero, automating bookkeeping workflows, syncing accounting data, or pulling financial reports programmatically.
> windsurf-rules
Configure Windsurf AI coding assistant with .windsurfrules and workspace rules. Use when: customizing Windsurf for a project, setting AI coding standards, creating team-shared Windsurf configurations, or tuning Cascade AI behavior.