glimfly
code updated

What is debounce and why does your search box wait?

In one sentence:Debounce is a technique that delays running a function until a burst of rapid calls, like keystrokes, goes quiet for a set pause, so it runs once instead of on every single event.

What it actually is

Debounce is a technique that delays running a function until a burst of rapid calls goes quiet for a set pause, then runs it once. Type five characters into a search box in half a second, and a debounced handler waits until you stop typing for, say, 300 milliseconds, then fires once with whatever you typed last.

Think of an elevator door. Someone steps in and the door starts a short closing timer. A second person steps in a moment later and that timer resets. The door waits until nobody has stepped in for a couple of seconds, then closes, whenever that ends up being. Debounce works the same way: each new event pushes the action back, and only a stretch of silence lets it through.

People swap debounce and throttle constantly, and the two solve the same complaint (an event fires too often) with opposite guarantees. Debounce waits for the events to stop before acting at all, so a debounced search box might fire zero times if you never pause. Throttle guarantees the action keeps running on a schedule while events keep coming, like a scroll handler updating position at most once every 200 milliseconds no matter how continuously you scroll. Reach for debounce when you only care about the final state (the search term you actually finished typing) and for throttle when you need to keep sampling while the activity is still happening.

Why your AI just did this

Your agent adds debounce anywhere a fast, repeating event would otherwise call an API endpoint or run an expensive check on every fire. Search-as-you-type boxes are the classic case: without it, each keystroke triggers a fresh request, and a handful of real users typing at once can trip a rate limit or burn through a paid API’s quota fast. Autosave fields, form validation that checks a username’s availability, and resize or scroll handlers get the same treatment.

Cursor, Claude Code, and Lovable typically reach for one of a few patterns: a small use-debounce hook, lodash’s _.debounce, or a hand-rolled setTimeout/clearTimeout pair inside a useEffect. Most only delay the network call. The input’s visible state still updates on every keystroke, so the text you type appears right away and only the fetch waits.

When you’ll run into it

The everyday sighting is a short pause between typing in a search box and results appearing. That’s debounce doing its job. Before it’s added, or with the wait set too short, you’ll see the opposite: the network tab in your browser’s dev tools filling with a request per keystroke, and eventually a 429 response once you hit a provider’s rate limit.

The confusing sighting is the one that feels like a bug: you type a search term, immediately click away or submit, and the last character or two never seem to register. A pending debounce timer got cancelled before it fired, because whatever triggered it (navigation, an unmount, a form submit) happened before the pause completed. Autosave hits the same trap: close a document seconds after your last edit and that edit may never have been saved.

What to check

  • Confirm the debounce is delaying the actual network call, not just the visible typing. If the input itself feels laggy, the wrong layer got debounced.
  • Check the wait value is reasonable. Search-as-you-type commonly sits between 300 and 500 milliseconds; much shorter barely helps, much longer feels broken.
  • Look for a cancel on unmount or navigation, so a debounce timer that never fires doesn’t quietly drop the user’s last input.
  • Verify a newer debounced request can’t get overwritten by a slower, older one that lands late; cancel the previous in-flight request when a new one starts.
  • If instant feedback matters more than the “final” server response, ask whether an optimistic UI update belongs here instead of, or alongside, the debounce.
  • For anything that needs to keep updating on a steady cadence while the user is still active, like a scroll position or a live counter, ask for throttle, not debounce.

Say it like a dev

Instead of: “the search box is spamming my API and burning through my quota” Say: “Add a debounce to the search input so the API call only fires 300 milliseconds after I stop typing.”

Instead of: “the last thing I typed doesn’t show up in search” Say: “Check whether the debounce timer is getting cancelled by navigation or an unmount before it fires. That’s likely why my last keystroke isn’t triggering a search.”

Instead of: “make the scroll handler less laggy” Say: “This should be throttled, not debounced. I need it updating on a steady interval the whole time I’m scrolling, not just once after I stop.”

People actually ask

“Why does my search box fire an API call on every keystroke and eat my quota?”

Because nothing is telling it to wait. Without debounce, an `onChange` handler fires on every keystroke and each one triggers a new request, so typing a five-letter word sends five calls instead of one. Adding a debounce delays that call until you've paused typing for a few hundred milliseconds, so it fires once with your finished search term.

“What's the difference between debounce and throttle?”

Debounce waits for a burst of events to stop before acting at all, and might fire zero times if the events never stop. Throttle guarantees the action still runs on a schedule while events keep coming, like at most once every 200 milliseconds during a continuous scroll. Use debounce when you only care about the final result (the finished search term); use throttle when you need to keep sampling while the activity is still happening.

“Why did my last few keystrokes not trigger a search?”

A debounced call is a pending timer, and if you navigate away, submit the form, or the component unmounts before that timer finishes, the call gets cancelled before it ever fires. The keystroke registered fine; the timer built to act on it just never got the chance to finish.

Related terms

Checked against

free tool · no signup

Just met this in a real session? Glim reads it in plain words.

Paste what your agent just did, a git diff, your terminal, or its summary, and Glim tells you what changed and what to check before you ship. Nothing stored.

Explain my session →