use-simple-debounce Simple, dependency-free debouncing v.3.0.0 | Looking for v2?

import { useDebounce } from 'use-simple-debounce';

function SearchComponent() {
  const debouncedSearch = useDebounce((query: string) => performSearch(query), 300);

  const handleSearch = (query: string) => {
    // Optional: cleanup is handled by the library
    const cancel = debouncedSearch(query);
  };

  return (
    <input
      type="text"
      onChange={e => handleSearch(e.target.value)}
      placeholder="Search..."
    />
  );
}
import { useDebounce } from 'use-simple-debounce';

function SearchComponent() {
  const debouncedSearch = useDebounce((query: string) => performSearch(query), 300);

  const handleSearch = (query: string) => {
    // Optional: cleanup is handled by the library
    const cancel = debouncedSearch(query);
  };

  return (
    <input
      type="text"
      onChange={e => handleSearch(e.target.value)}
      placeholder="Search..."
    />
  );
}
<template>
  <input v-model="query" @input="handleInputChange" placeholder="Search..." />
</template>

<script setup>
import { ref } from 'vue';
import { useDebounce } from 'use-simple-debounce/vue';

const query = ref('');
const debouncedSearch = useDebounce((query: string) => performSearch(query), 300);

const handleInputChange = () => {
  // Optional: cleanup is handled by the library
  const cancel = debouncedSearch(query.value);
};
</script>
<template>
  <input v-model="query" @input="handleInputChange" placeholder="Search..." />
</template>

<script setup>
import { ref } from 'vue';
import { useDebounce } from 'use-simple-debounce/vue';

const query = ref('');
const debouncedSearch = useDebounce((query: string) => performSearch(query), 300);

const handleInputChange = () => {
  // Optional: cleanup is handled by the library
  const cancel = debouncedSearch(query.value);
};
</script>
import { createDebounce } from 'use-simple-debounce/solid';

function SearchComponent() {
  const debouncedSearch = createDebounce((query: string) => performSearch(query), 300);

  const handleSearch = (query: string) => {
    // Optional: cleanup is handled by the library
    const cancel = debouncedSearch(query);
  };

  return (
    <input
      type="text"
      onInput={e => handleSearch(e.target.value)}
      placeholder="Search..."
    />
  );
}
import { createDebounce } from 'use-simple-debounce/solid';

function SearchComponent() {
  const debouncedSearch = createDebounce((query: string) => performSearch(query), 300);

  const handleSearch = (query: string) => {
    // Optional: cleanup is handled by the library
    const cancel = debouncedSearch(query);
  };

  return (
    <input
      type="text"
      onInput={e => handleSearch(e.target.value)}
      placeholder="Search..."
    />
  );
}
<script>
  import { createDebounce } from 'use-simple-debounce/svelte';

  let query = '';
  const debouncedSearch = createDebounce((value) => performSearch(value), 300);

  function handleInputChange(event) {
    const value = event.target.value;
    // Optional: cleanup is handled by the library
    const cancel = debouncedSearch(value);
  }
</script>

<input bind:value={query} on:input={handleInputChange} placeholder="Search..." />
<script>
  import { createDebounce } from 'use-simple-debounce/svelte';

  let query = '';
  const debouncedSearch = createDebounce((value) => performSearch(value), 300);

  function handleInputChange(event) {
    const value = event.target.value;
    // Optional: cleanup is handled by the library
    const cancel = debouncedSearch(value);
  }
</script>

<input bind:value={query} on:input={handleInputChange} placeholder="Search..." />
<script>
  import { createDebounce } from 'use-simple-debounce/svelte';

  let query = $state('');
  const debouncedSearch = createDebounce((value) => performSearch(value), 300);

  function handleInputChange(event) {
    const value = event.target.value;
    // Optional: cleanup is handled by the library
    const cancel = debouncedSearch(value);
  }
</script>

<input bind:value={query} oninput={handleInputChange} placeholder="Search..." />
<script>
  import { createDebounce } from 'use-simple-debounce/svelte';

  let query = $state('');
  const debouncedSearch = createDebounce((value) => performSearch(value), 300);

  function handleInputChange(event) {
    const value = event.target.value;
    // Optional: cleanup is handled by the library
    const cancel = debouncedSearch(value);
  }
</script>

<input bind:value={query} oninput={handleInputChange} placeholder="Search..." />
import { debounce } from 'use-simple-debounce/vanilla';

const debouncedSearch = debounce((query) => performSearch(query), 300);

function handleSearch(query) {
  // Cleanup (if needed) should be executed manually - no automatic cleanup
  // Example: cancel()
  const cancel = debouncedSearch(query);
}

const input = document.querySelector('input');
input.addEventListener('input', e => handleSearch(e.target.value));
import { debounce } from 'use-simple-debounce/vanilla';

const debouncedSearch = debounce((query) => performSearch(query), 300);

function handleSearch(query) {
  // Cleanup (if needed) should be executed manually - no automatic cleanup
  // Example: cancel()
  const cancel = debouncedSearch(query);
}

const input = document.querySelector('input');
input.addEventListener('input', e => handleSearch(e.target.value));

A lightweight debounce utility for React, Solid, Svelte, Vue, and vanilla JavaScript. Zero dependencies, TypeScript support, and async function handling.

Zero dependencies
TypeScript support
Async function support

Inner Workings

Curious about how it works under the hood? Here's the complete source code for each framework implementation.

import { useRef, useEffect } from 'react';

export function useDebounce<F extends (...args: Parameters<F>) => ReturnType<F>>(
  func: F,
  wait: number = 300
): (...args: Parameters<F>) => () => void {
  const timeout = useRef<ReturnType<typeof setTimeout> | null>(null);

  useEffect(() => {
    return () => {
      if (timeout.current) {
        clearTimeout(timeout.current);
        timeout.current = null;
      }
    };
  }, []);

  return (...args: Parameters<F>) => {
    if (timeout.current) {
      clearTimeout(timeout.current);
    }
    timeout.current = setTimeout(() => {
      func(...args);
    }, wait);

    return () => {
      if (timeout.current) {
        clearTimeout(timeout.current);
        timeout.current = null;
      }
    };
  };
}
import { useRef, useEffect } from 'react';

export function useDebounce<F extends (...args: Parameters<F>) => ReturnType<F>>(
  func: F,
  wait: number = 300
): (...args: Parameters<F>) => () => void {
  const timeout = useRef<ReturnType<typeof setTimeout> | null>(null);

  useEffect(() => {
    return () => {
      if (timeout.current) {
        clearTimeout(timeout.current);
        timeout.current = null;
      }
    };
  }, []);

  return (...args: Parameters<F>) => {
    if (timeout.current) {
      clearTimeout(timeout.current);
    }
    timeout.current = setTimeout(() => {
      func(...args);
    }, wait);

    return () => {
      if (timeout.current) {
        clearTimeout(timeout.current);
        timeout.current = null;
      }
    };
  };
}
import { ref, onUnmounted } from 'vue';

export function useDebounce<F extends (...args: Parameters<F>) => ReturnType<F>>(
  func: F,
  wait: number = 300
): (...args: Parameters<F>) => () => void {
  const timeout = ref<ReturnType<typeof setTimeout> | null>(null);

  onUnmounted(() => {
    if (timeout.value) {
      clearTimeout(timeout.value);
      timeout.value = null;
    }
  });

  return (...args: Parameters<F>) => {
    if (timeout.value) {
      clearTimeout(timeout.value);
    }
    timeout.value = setTimeout(() => {
      func(...args);
    }, wait);

    return () => {
      if (timeout.value) {
        clearTimeout(timeout.value);
        timeout.value = null;
      }
    };
  };
}
import { ref, onUnmounted } from 'vue';

export function useDebounce<F extends (...args: Parameters<F>) => ReturnType<F>>(
  func: F,
  wait: number = 300
): (...args: Parameters<F>) => () => void {
  const timeout = ref<ReturnType<typeof setTimeout> | null>(null);

  onUnmounted(() => {
    if (timeout.value) {
      clearTimeout(timeout.value);
      timeout.value = null;
    }
  });

  return (...args: Parameters<F>) => {
    if (timeout.value) {
      clearTimeout(timeout.value);
    }
    timeout.value = setTimeout(() => {
      func(...args);
    }, wait);

    return () => {
      if (timeout.value) {
        clearTimeout(timeout.value);
        timeout.value = null;
      }
    };
  };
}
import { onCleanup } from 'solid-js';

export function createDebounce<F extends (...args: Parameters<F>) => ReturnType<F>>(
  func: F,
  wait: number = 300
): (...args: Parameters<F>) => () => void {
  let timeout: ReturnType<typeof setTimeout> | null = null;

  onCleanup(() => {
    if (timeout) {
      clearTimeout(timeout);
      timeout = null;
    }
  });

  return (...args: Parameters<F>) => {
    if (timeout) {
      clearTimeout(timeout);
    }
    timeout = setTimeout(() => {
      func(...args);
    }, wait);

    return () => {
      if (timeout) {
        clearTimeout(timeout);
        timeout = null;
      }
    };
  };
}
import { onCleanup } from 'solid-js';

export function createDebounce<F extends (...args: Parameters<F>) => ReturnType<F>>(
  func: F,
  wait: number = 300
): (...args: Parameters<F>) => () => void {
  let timeout: ReturnType<typeof setTimeout> | null = null;

  onCleanup(() => {
    if (timeout) {
      clearTimeout(timeout);
      timeout = null;
    }
  });

  return (...args: Parameters<F>) => {
    if (timeout) {
      clearTimeout(timeout);
    }
    timeout = setTimeout(() => {
      func(...args);
    }, wait);

    return () => {
      if (timeout) {
        clearTimeout(timeout);
        timeout = null;
      }
    };
  };
}
import { onDestroy } from 'svelte';

export function createDebounce<F extends (...args: Parameters<F>) => ReturnType<F>>(
  func: F,
  wait: number = 300
): (...args: Parameters<F>) => () => void {
  let timeout: ReturnType<typeof setTimeout> | null = null;

  onDestroy(() => {
    if (timeout) {
      clearTimeout(timeout);
      timeout = null;
    }
  });

  return (...args: Parameters<F>) => {
    if (timeout) {
      clearTimeout(timeout);
    }
    timeout = setTimeout(() => {
      func(...args);
    }, wait);

    return () => {
      if (timeout) {
        clearTimeout(timeout);
        timeout = null;
      }
    };
  };
}
import { onDestroy } from 'svelte';

export function createDebounce<F extends (...args: Parameters<F>) => ReturnType<F>>(
  func: F,
  wait: number = 300
): (...args: Parameters<F>) => () => void {
  let timeout: ReturnType<typeof setTimeout> | null = null;

  onDestroy(() => {
    if (timeout) {
      clearTimeout(timeout);
      timeout = null;
    }
  });

  return (...args: Parameters<F>) => {
    if (timeout) {
      clearTimeout(timeout);
    }
    timeout = setTimeout(() => {
      func(...args);
    }, wait);

    return () => {
      if (timeout) {
        clearTimeout(timeout);
        timeout = null;
      }
    };
  };
}
export function debounce<F extends (...args: Parameters<F>) => ReturnType<F>>(
  func: F,
  wait: number
): (...args: Parameters<F>) => () => void {
  let timeout: ReturnType<typeof setTimeout> | null = null;

  return (...args: Parameters<F>) => {
    if (timeout) {
      clearTimeout(timeout);
    }

    timeout = setTimeout(() => {
      func(...args);
    }, wait);

    return () => {
      if (timeout) {
        clearTimeout(timeout);
        timeout = null;
      }
    };
  };
}
export function debounce<F extends (...args: Parameters<F>) => ReturnType<F>>(
  func: F,
  wait: number
): (...args: Parameters<F>) => () => void {
  let timeout: ReturnType<typeof setTimeout> | null = null;

  return (...args: Parameters<F>) => {
    if (timeout) {
      clearTimeout(timeout);
    }

    timeout = setTimeout(() => {
      func(...args);
    }, wait);

    return () => {
      if (timeout) {
        clearTimeout(timeout);
        timeout = null;
      }
    };
  };
}