A simple, dependency-free debounce utility for React, Solid, Svelte, Vue, and vanilla JavaScript with async support.
For those who like simple things: https://use-simple-debounce.jujiplay.com/
setTimeout for reliable debouncingnpm install use-simple-debounce
API Change: The debounce API has been simplified and improved. Instead of creating a debouncer instance and calling it with a function, you now pass the function directly to the debounce creator.
Migration Guide:
// Old API (v2.x)
const debounced = useDebounce();
debounced(() => performSearch(query), 300);
// New API (v3.x)
const debouncedSearch = useDebounce((query) => performSearch(query), 300);
debouncedSearch(query);
The new API is more intuitive and provides better TypeScript support with automatic argument inference.
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 { createSignal } from 'solid-js';
import { createDebounce } from 'use-simple-debounce/solid';
function SearchComponent() {
const [query, setQuery] = createSignal('');
const debouncedSearch = createDebounce((query: string) => performSearch(query), 300);
const handleSearch = (query: string) => {
setQuery(query);
// Optional: cleanup is handled by the library
const cancel = debouncedSearch(query);
};
return (
<input
type="text"
value={query()}
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;
query = value;
// Optional: cleanup is handled by the library
const cancel = debouncedSearch(value);
}
</script>
<input
type="text"
bind:value={query}
on:input={handleInputChange}
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>
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 { useDebounce } from 'use-simple-debounce';
function AutoSaveEditor() {
const [content, setContent] = useState('');
const debouncedSave = useDebounce(async (newContent: string) => {
try {
// Save after 1 second of inactivity
await saveToServer(newContent);
console.log('Auto-saved!');
} catch (err) {
console.error('Save failed:', err);
}
}, 1000);
const handleChange = (newContent: string) => {
setContent(newContent);
// Optional: cleanup is handled by the library
const cancel = debouncedSave(newContent);
};
return (
<textarea
value={content}
onChange={(e) => handleChange(e.target.value)}
placeholder="Start typing..."
/>
);
}
import { useDebounce } from 'use-simple-debounce';
function SearchComponent() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const debouncedSearch = useDebounce(async (searchQuery: string) => {
const response = await fetch(`/api/search?q=${searchQuery}`);
const data = await response.json();
setResults(data.results);
}, 300);
const handleInputChange = (value: string) => {
setQuery(value);
// Optional: cleanup is handled by the library
const cancel = debouncedSearch(value);
};
return (
<div>
<input
type="text"
value={query}
onChange={(e) => handleInputChange(e.target.value)}
placeholder="Search..."
/>
<ul>
{results.map((result) => (
<li key={result.id}>{result.title}</li>
))}
</ul>
</div>
);
}
debounce() / useDebounce() / createDebounce()Creates a debounced function that accepts arguments and returns an optional cancel function.
Parameters:
fn: F - The function to debouncewait?: number - The delay in milliseconds (default: 300ms)Returns: A debounced function that accepts the same arguments as the original function and returns an optional cancel function.
Type:
function debounce<F extends (...args: any[]) => any>(
fn: F,
wait?: number
): (...args: Parameters<F>) => (() => void) | undefined;
Framework Usage:
import { useDebounce } from 'use-simple-debounce'import { createDebounce } from 'use-simple-debounce/solid'import { createDebounce } from 'use-simple-debounce/svelte'import { useDebounce } from 'use-simple-debounce/vue'import { debounce } from 'use-simple-debounce/vanilla'Usage Examples:
// Create debounced function
const debouncedSearch = debounce((query: string) => performSearch(query), 300);
// Call with arguments - returns optional cancel function
const cancel = debouncedSearch('hello world');
// Cancel if needed (optional - cleanup is handled automatically by frameworks)
if (cancel) cancel();
300msThe most frequently used delay across React applications is 300ms - it provides the best balance between responsiveness and performance for most user interactions.
| Use Case | Recommended Delay | Reason |
|---|---|---|
| Search/Autocomplete | 300ms โญ |
Most common - balances UX with API efficiency |
| Form Validation | 300-500ms |
Give users time to finish typing |
| Auto-save | 1000-2000ms |
Allow time for continuous editing |
| Window Resize | 150-250ms |
Handle rapid resize events smoothly |
| Scroll Events | 100-200ms |
Maintain smooth scrolling experience |
| API Calls | 300-600ms |
Prevent excessive server requests |
Tip: Start with 300ms for user input scenarios - it's the sweet spot used by most major applications!
MIT ยฉ juji
Contributions welcome! Please feel free to submit a Pull Request.