Skip to main content
Beta: Front-End Checklist is currently in beta. Some issues are still being fixed. Thanks for your patience.

Debounce and throttle event handlers

Use debounce or throttle for high-frequency events like scroll, resize, and input to improve performance.

Utilities
Quick take
Typical fix time 15 min
  • Debounce: delays execution until activity stops (search inputs, form validation)
  • Throttle: limits execution rate (scroll, resize handlers)
  • Use 150-300ms delay for user input, 100ms for scroll/resize
  • Clean up event listeners to prevent memory leaks
Why it matters: High-frequency events fire hundreds of times per second, causing UI jank, excessive API calls, and poor Interaction to Next Paint (INP) scores.

Rule Details

High-frequency events like scroll, resize, and input can fire hundreds of times per second, causing performance issues if handlers are not rate-limited.

Code Examples

Debounce (wait for pause)

function debounce(func, wait) {
  let timeout
  return function executedFunction(...args) {
    clearTimeout(timeout)
    timeout = setTimeout(() => func.apply(this, args), wait)
  }
}
 
// Usage: Search input
const searchInput = document.querySelector('#search')
const handleSearch = debounce((e) => {
  fetchSearchResults(e.target.value)
}, 300)
 
searchInput.addEventListener('input', handleSearch)

Throttle (limit rate)

function throttle(func, limit) {
  let inThrottle
  return function executedFunction(...args) {
    if (!inThrottle) {
      func.apply(this, args)
      inThrottle = true
      setTimeout(() => inThrottle = false, limit)
    }
  }
}
 
// Usage: Scroll handler
const handleScroll = throttle(() => {
  updateScrollProgress()
}, 100)
 
window.addEventListener('scroll', handleScroll)

Why It Matters

High-frequency events fire hundreds of times per second, causing UI jank, excessive API calls, and poor Interaction to Next Paint (INP) scores.

When to Use Each

PatternUse CaseExample
DebounceWait for pause in activitySearch input, form validation
ThrottleLimit execution rateScroll position, resize handlers

Framework Examples

React

import { useMemo, useCallback } from 'react'
import { debounce } from 'lodash-es'
 
function SearchComponent() {
  const [query, setQuery] = useState('')
 
  // Memoize debounced function
  const debouncedSearch = useMemo(
    () => debounce((value) => {
      fetchResults(value)
    }, 300),
    []
  )
 
  // Cleanup on unmount
  useEffect(() => {
    return () => {
      debouncedSearch.cancel()
    }
  }, [debouncedSearch])
 
  const handleChange = (e) => {
    setQuery(e.target.value)
    debouncedSearch(e.target.value)
  }
 
  return <input value={query} onChange={handleChange} />
}

Vue 3

<script setup>
import { ref, onUnmounted } from 'vue'
import { useDebounceFn } from '@vueuse/core'
 
const query = ref('')
const results = ref([])
 
const search = useDebounceFn(async (value) => {
  results.value = await fetchResults(value)
}, 300)
 
function handleInput(e) {
  query.value = e.target.value
  search(e.target.value)
}
</script>
 
<template>
  <input :value="query" @input="handleInput" />
</template>

Common Mistakes

// ❌ Bad: Creating new debounced function on every render
function Component() {
  const handleInput = debounce((e) => {
    search(e.target.value)
  }, 300) // New function every render!
}
 
// ❌ Bad: Not cleaning up debounced calls
useEffect(() => {
  // No cleanup for pending debounced calls
}, [])
 
// ❌ Bad: Debouncing inside the handler
element.addEventListener('scroll', () => {
  debounce(updateUI, 100)() // Creates new debounce each time!
})

Standards

  • Use MDN: JavaScript Guide as the standard for how this JavaScript pattern should behave in production, not just in a small local example.
  • Use web.dev: Learn JavaScript as the standard for how this JavaScript pattern should behave in production, not just in a small local example.

Verification

  1. Verify the behavior in the browser after the code change, not only in static analysis.
  2. Inspect DevTools Network or Performance panels when the rule affects loading or execution order.
  3. Test the primary user flow and one edge case triggered by the changed script path.
  4. Confirm the code still behaves correctly when the feature is delayed, lazy-loaded, or fails.

Use with AI

Copy these prompts to use with your AI assistant, or install the MCP server to use directly from Claude, Cursor, or Windsurf.

Check

Verify implementation

Review this JavaScript code for scroll, resize, input, mousemove, or other high-frequency event handlers that should use debounce or throttle.

Fix

Auto-fix issues

Add debounce or throttle to high-frequency event handlers to limit execution rate and improve performance.

Explain

Learn more

Explain the difference between debounce and throttle, and when to use each pattern for optimal performance.

Review

Code review

Review scripts, client components, and browser execution paths related to Debounce and throttle event handlers. Flag exact imports, event handlers, runtime side effects, or blocking operations that violate the rule, and state how the change should be verified in the browser.

Sources

References used to support the guidance in this rule.

Further Reading

Tools and supplementary material for exploring the topic in more depth.

Chrome DevTools
developer.chrome.comTool

Rules that often go hand-in-hand with this one.

Prevent common memory leak patterns

Identify and avoid the most common JavaScript memory leak sources: forgotten event listeners, retained DOM references, closures holding large objects, and uncleared timers.

JavaScript
Optimize interaction to next paint

Page responds to user interactions within 200ms, ensuring good responsiveness.

Performance
Minify all JavaScript files

All JavaScript files are minified to reduce file size and improve loading performance.

JavaScript
Never use eval() or unsafe dynamic code execution

Avoid eval(), new Function(), setTimeout/setInterval with string arguments, and innerHTML with untrusted content — they execute arbitrary code and create critical XSS vulnerabilities.

JavaScript

Was this rule helpful?

Your feedback helps improve rule quality. This stays internal for now.

Loading feedback...
0 / 385