Back to Client-Side Data Fetching

Deduplicate Global Event Listeners

clientswrevent-listenerssubscription
28.0k🕒 2026-06-10Source ↗

Install this skill

npx skills add vercel-labs/agent-skills

Works across Claude Code, Cursor, Codex, Copilot & Antigravity

The Deduplicate Global Event Listeners skill optimizes React applications by centralizing window-level event handling. Standard implementations of event listeners in hooks often create redundant bindings whenever components remount or multiple instances of the same hook are active, leading to memory bloat and performance degradation. By integrating SWR's subscription model with a centralized registry, this pattern ensures that a single event listener handles all active callbacks for a specific event type. This approach prevents the 'N instances equal N listeners' problem, consolidating cross-component communication into one window event stream. It maintains granular control over individual callbacks while reducing the total number of browser-level event attachments, resulting in cleaner event propagation and more predictable behavior during complex user interactions like keyboard shortcuts or global window resizing.

When to Use This Skill

  • Implementing global keyboard shortcuts across distributed components
  • Managing multiple window resize tracking hooks without performance drops
  • Monitoring mouse tracking state for interactive dashboard elements
  • Synchronizing browser focus/blur events across independent widgets

How to Invoke This Skill

Example prompts that trigger this skill in Claude Code, Cursor, or Antigravity:

  • Optimize my global event listeners in React
  • How do I prevent duplicate window event listeners
  • Use SWR to manage event listener lifecycle
  • Centralize window event handlers for multiple components
  • Fix memory usage of useEffect keyboard shortcuts

What this skill does

  • Consolidates multiple window event listeners into a single attachment
  • Uses a central Map registry to manage active subscriber callbacks
  • Integrates SWR subscription lifecycle with native DOM event handling
  • Prevents memory leaks from orphaned window event listeners
  • Supports multiple concurrent subscribers for a single event type

When not to use it

  • When components require unique event capture phases or specific stopPropagation logic
  • In simple applications where only one instance of an event listener is ever created
  • If the event frequency is low enough that standard useEffect listeners do not impact performance

Example workflow

  1. Define a module-level Map to store callback sets keyed by event identifier
  2. Create a custom hook that registers the current callback into the Map on mount
  3. Implement the useSWRSubscription hook to attach a single shared window listener
  4. Update the shared handler to iterate over the Map and execute relevant callbacks
  5. Cleanup the registry on component unmount to prevent stale callback execution

Prerequisites

  • swr library
  • React Hooks knowledge
  • Understanding of DOM event propagation

Pitfalls & limitations

  • !Forgetting to clean up the Set entry in the Map leads to memory leaks
  • !Closure staleness if callbacks rely on out-of-date state values
  • !The central registry is module-scoped, so it persists for the lifetime of the application

FAQ

Why not just use useEffect?
While useEffect manages individual listeners well, it creates one listener per hook instance, which becomes inefficient when you have many components listening for the same global event.
Does this support event removal?
Yes, the cleanup function in the useEffect hook removes the specific callback from the set, and the listener logic should handle removing the global listener if the set becomes empty.
Is this pattern thread-safe?
JavaScript is single-threaded, so the module-scoped Map is safe from race conditions, but ensure your callbacks are wrapped in useCallback if dependencies change frequently.
Can I use this for non-global events?
This pattern is specifically designed for window-level global events; using it for scoped DOM elements requires changing the event target from window to the specific element reference.

How it compares

Manually attaching listeners in every component results in linear performance degradation, whereas this subscription pattern ensures constant complexity regardless of the number of active components.

Source & trust

28k stars🕒 Updated 2026-06-10
📄 Full skill instructions — original source: vercel-labs/agent-skills
## Deduplicate Global Event Listeners

Use useSWRSubscription() to share global event listeners across component instances.

**Incorrect (N instances = N listeners):**

function useKeyboardShortcut(key: string, callback: () => void) {
useEffect(() => {
const handler = (e: KeyboardEvent) => {
if (e.metaKey && e.key === key) {
callback()
}
}
window.addEventListener('keydown', handler)
return () => window.removeEventListener('keydown', handler)
}, [key, callback])
}


When using the useKeyboardShortcut hook multiple times, each instance will register a new listener.

**Correct (N instances = 1 listener):**

import useSWRSubscription from 'swr/subscription'

// Module-level Map to track callbacks per key
const keyCallbacks = new Map<string, Set<() => void>>()

function useKeyboardShortcut(key: string, callback: () => void) {
// Register this callback in the Map
useEffect(() => {
if (!keyCallbacks.has(key)) {
keyCallbacks.set(key, new Set())
}
keyCallbacks.get(key)!.add(callback)

return () => {
const set = keyCallbacks.get(key)
if (set) {
set.delete(callback)
if (set.size === 0) {
keyCallbacks.delete(key)
}
}
}
}, [key, callback])

useSWRSubscription('global-keydown', () => {
const handler = (e: KeyboardEvent) => {
if (e.metaKey && keyCallbacks.has(e.key)) {
keyCallbacks.get(e.key)!.forEach(cb => cb())
}
}
window.addEventListener('keydown', handler)
return () => window.removeEventListener('keydown', handler)
})
}

function Profile() {
// Multiple shortcuts will share the same listener
useKeyboardShortcut('p', () => { /* ... */ })
useKeyboardShortcut('k', () => { /* ... */ })
// ...
}

How to Use This Skill Unit

Option A: Project-Specific (Recommended)

  1. Click "Download" above
  2. In your project, create the directory: .agent/skills/client-event-listeners/
  3. Save the file as SKILL.md
  4. The agent will automatically discover the skill based on its description.

Option B: Global Installation (All Agents)

Save the file to these locations to make it available across all projects:

  • Claude Code: ~/.claude/skills/vercel-labs/agent-skills/client-event-listeners/SKILL.md
  • Cursor: ~/.cursor/skills/vercel-labs/agent-skills/client-event-listeners/SKILL.md
  • Antigravity: ~/.gemini/antigravity/skills/vercel-labs/agent-skills/client-event-listeners/SKILL.md

🚀 Install with CLI:
npx skills add vercel-labs/agent-skills

Read the Master Guide: Mastering Agent Skills

Recommended Rules

View more rules

Recommended Workflows

View more workflows

Recommended MCP Servers

View more MCP servers

Take It Further

Maximize your productivity with these powerful resources

📋

Define Your Standards

Set up coding standards to ensure this workflow produces consistent, high-quality results.

Browse Rules Library
📖

Master Workflows

Learn how to create custom workflows, use Turbo Mode, and build your automation library.

Complete Guide

How to use this Skill in Claude Code & Cursor

For Claude Code (CLI)

To use this skill in Claude Code, copy the rule content into your project's custom instructions or follow our Add-Skill CLI guide. This ensures Claude follows your standards during every code generation.

For Cursor & Windsurf

For Cursor or Windsurf, individual skills are best used in the "Rules for AI" section. This specific unit helps the agent avoid client-side data fetching issues, leading to cleaner, more efficient code.

Why the skill format matters: the standardized Agent Skills format lets your AI agent load detailed instructions only when they are relevant, keeping your prompt clean while improving results.

Source & attribution

This skill is categorized under Client-Side Data Fetching and is published by Vercel Engineering, maintained in vercel-labs/agent-skills.

← Browse All Agent Skills
Sponsored AI assistant. Recommendations may be paid.