Library / SDK
sindresorhus/debounce avatar
sindresorhus/debounce

debounce is 40 lines of JavaScript with the control methods people forget

Delay function calls until a set time elapses after the last invocation

851 stars76 forksJavaScriptNOASSERTION

At a glance

What is it?
debounce delays a function call until a set time has passed since the last invocation, and the version 3 package adds isPending, clear, flush and trigger on the returned function. It is ESM only, requires Node 20 or newer, and its licence is MIT in the package metadata while the repository file is one GitHub cannot classify.
Who is it for?
debounce is a good fit for a JavaScript project that needs to collapse a burst of events, such as resize, input or scroll, into one call, and it is worth using version 3 when you need flush and trigger because a search box that cannot be flushed on submit is a bug you will write yourself.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 40 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One function, one timer, and the last call wins

debounce does one thing. The package description is the whole contract: delay function calls until a set time elapses after the last invocation.

That last clause is the part that trips people up. A debounced function does not run on a fixed schedule and it does not run on the first call. It waits for the calls to stop, and the wait restarts on every new call. The README's API section says the same thing in different words, that the created function delays execution until wait milliseconds have passed since its last invocation.

The classic case is a resize handler that would otherwise run on every pixel of a drag. The README's example is exactly that.

js
import debounce from 'debounce';

function resize() {
	console.log('height', window.innerHeight);
	console.log('width', window.innerWidth);
}

window.onresize = debounce(resize, 200);

Note the shape of the call. You pass the function and the wait in milliseconds, and you get back a function that you can assign anywhere. There is no separate start and stop call and no object to manage.

The package is a single file. The repository root holds index.js, index.d.ts, a test.js and a package.json, and the files array in the metadata publishes only the implementation and the type declarations, so nothing else reaches your node_modules.

isPending, clear, flush and trigger are the reason to use this package

The interesting part of the API is the four members on the returned function, because a hand rolled debounce usually gives you none of them and you end up writing them yourself badly.

isPending reports whether the debounce delay is currently active, so your code can tell the difference between idle and scheduled.

js
window.onresize.isPending;

clear cancels any scheduled executions and resets the timer, which is what you want when a component unmounts and the pending call should not fire against a detached element.

js
window.onresize.clear();

flush runs the scheduled call immediately if one is scheduled, and clears the timer. This is the one that matters in practice. A search field whose debounced query is pending when the user presses enter will show results for a term they have already replaced, and flush is the fix.

js
window.onresize.flush();

trigger is close to flush but unconditional. It executes the function immediately and clears the timer if one was set, whether or not anything was pending.

js
window.onresize.trigger();

The distinction between the last two is small but real. flush is conditional on there being something scheduled, trigger is not, so trigger always produces a call.

The immediate option covers double clicks but changes the timing

There is one option, immediate, and it is worth understanding before you reach for it.

Set immediate to true and the function executes immediately at the start of the wait interval, rather than at the end. The README gives the reason: it prevents issues such as double-clicks on a button.

That is the standard pattern for a submit button. With a trailing debounce, a user who clicks twice quickly gets one request, but only after the wait, and the button appears dead in the meantime. With immediate, the first click fires at once and the second click inside the window is swallowed.

The consequence is that the semantics of the delay invert. A trailing debounce coalesces a burst into a single call at the end of the burst. An immediate debounce fires at the start of the burst and suppresses the rest. For a resize handler you want the trailing behaviour, because the final dimensions are the ones that matter. For a destructive action button you want the leading behaviour, because the action should feel instant and the guard should be about double submission.

The package does not offer a trailing flag for the immediate case, so if you need both a leading and a trailing call in one window, that is not something this module provides.

Installing debounce and using it in an ESM project

The install is the usual one.

sh
npm install debounce

The import in the README is a default import, and the package is published as an ES module. The package.json sets type to module, and the exports map has two entries, types pointing at index.d.ts and default pointing at index.js, with no CommonJS fallback listed. There is no require entry in the export map, so a CommonJS project cannot require this package by name.

The types are shipped, so a TypeScript project gets the signatures without installing a separate types package, and the type declaration file sits at the root of the repository rather than in a types subfolder.

The engines field requires Node 20 or newer, which is a meaningful constraint if your build still targets older runtimes, since the package does not attempt to work around an older Node.

Running the tests locally is a single command, which lints and then runs the node test runner.

bash
npm test

The script runs xo and then node --test, and the xo dependency is the only devDependency in the package, so the test surface is the standard Node one rather than a separate framework.

Version 3 broke the CommonJS world and needs Node 20

The release history shows a major version landing recently. v3.0.0 was published on 2025-11-02, after v2.2.0 from 2024-10-14 and v2.1.1 from 2024-09-09.

A major bump for a package this small usually means a breaking change, and the metadata points at two of them. The package is ESM only, with type set to module and no require condition in the exports map, and the engines field has been raised to Node 20 or newer.

The README does not include a migration note from version 2, so if you are upgrading an existing project, check the release diff rather than assuming the import line is unchanged. What the current documentation does tell you is the current shape: a default export, a single function argument pair, and the four control methods on the result.

The package is marked sideEffects false, which lets a bundler drop the import entirely if nothing from it is used, and the keyword list in the metadata covers the terms people search for, from debounce and debouncing to rate limiting and batching.

The repository is not archived and the last push was on 2026-09-18, so the code is being worked on after the 3.0.0 release rather than sitting still.

Debounce is the wrong tool for a rate limit or a promise

Debouncing solves one specific problem and creates three others if you reach for it too broadly.

The first is a maximum wait. A debounced function never runs while calls keep arriving, so a stream of events can starve it indefinitely. If you need a guarantee that the function runs at least every n milliseconds regardless of traffic, you want a maximum wait, which this package does not have. The README points to debounce-fn as the similar package with more options such as maxWait and before and after hooks.

The second is promises. This module is synchronous and has no notion of a returned promise, so awaiting a debounced function does the wrong thing. For async work the README points to p-debounce, which handles promises.

The third is rate limiting. Debounce delays the last call; it does not limit how often the function executes over a window. If the requirement is no more than one call per second regardless of input rate, that is throttling, and the README lists throttleit for exactly that purpose.

A fourth, quieter limitation is cancellation of in-flight work. Debounce schedules a call and lets it happen; it cannot abort a request that has already started. If your use case is a live search that should ignore responses from an older query, debouncing the input is not enough, because the ordering of the responses still has to be handled separately.

The licence file and the metadata disagree on paper

The package.json declares the licence as MIT, with a funding link to the author's sponsorship page, and the repository ships a file named license in lowercase. GitHub's classification of the repository licence is recorded as a custom one that it cannot categorise, which is a difference worth resolving before you ship the package inside a product.

The practical advice is simple. Read the license file in the repository rather than trusting the metadata string, because a bundler, a license scanner and a legal reviewer will all look at different places, and only one of them is the actual text. A custom classification is not automatically a restrictive licence, and it is not automatically a permissive one either.

Everything else about the package is deliberately minimal. There is no dependency, not even a transitive one, the implementation is a single file, and the published tarball contains only index.js and index.d.ts. For a utility this small that is the right shape, and it also means there is no supply chain surface to review beyond the package itself.

Editorial conclusion

debounce is a good fit for a JavaScript project that needs to collapse a burst of events, such as resize, input or scroll, into one call, and it is worth using version 3 when you need flush and trigger because a search box that cannot be flushed on submit is a bug you will write yourself. It is the wrong tool when you need a maximum wait, when the function returns a promise, or when you want to limit execution rate rather than delay it, since the README points to other packages for each of those. Before adopting it, check that your build can consume ES modules and that your runtime is Node 20 or newer, then read the licence file in the repository, because the package metadata says MIT while the file on disk is a custom licence GitHub cannot classify.

Frequently asked questions

How do I install debounce?

Run npm install debounce and import the default export. The package is published as an ES module with type set to module, and the exports map lists only a types and a default condition, so there is no CommonJS entry point.

What Node version does debounce 3 require?

The engines field in package.json requires Node 20 or newer. Version 3.0.0 was published on 2025-11-02, and the README does not include a migration note from version 2.

How do I cancel a pending debounced call?

Call the clear method on the returned function. It cancels any scheduled executions, and isPending reports whether the delay is currently active.

What is the difference between flush and trigger on a debounced function?

flush executes the scheduled call immediately and clears the timer, but only if an execution is scheduled. trigger executes the function immediately and clears the timer if one was previously set, so it always produces a call.

What should I use instead of debounce when I need a maxWait or a promise?

The README lists debounce-fn as the similar package with more options such as maxWait and before and after hooks, and p-debounce as the one that handles promises. For limiting execution rate rather than delaying it, the README points to throttleit.

Official sources

  1. Issues
  2. README
  3. Releases
  4. sindresorhus/debounce on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/sindresorhus-debounce.svg)](https://hysenlabs.com/projects/sindresorhus-debounce)

Was this useful?