Debounced Input
An enhanced Input component that delays value updates, perfect for search fields and performance-sensitive forms.
Try typing below to see the debouncing effect.
Note
Check your console to see the debouncing effect.
Installation
Install Shadcn Input
This component is built on top of the Shadcn UI Input component. If you haven't installed it yet, run:
npx shadcn@latest add inputCreate the Component
Create a new file named debounced-input.tsx in your components folder (e.g., components/ui/debounced-input.tsx) and copy the code below.
"use client";
import { Input } from "@/components/ui/input";
import { cn } from "@/lib/utils";
import { useEffect, useState } from "react";
type DebouncedInputProps = {
debouncing?: boolean;
debouncingValue?: number;
onDebouncedChange?: (value: string) => void;
className?: string;
} & React.ComponentProps<typeof Input>;
const DebouncedInput = ({ debouncing, debouncingValue = 500, onDebouncedChange, className, ...props }: DebouncedInputProps) => {
const isControlled = props.value !== undefined;
// This is used so that user typing is not affected by the debouncing
const [input, setInput] = useState(
isControlled ?
(props.value as string) :
(props.defaultValue as string) ?? ""
);
useEffect(() => {
if (isControlled) {
setInput(props.value as string);
}
}, [props.value, isControlled]);
useEffect(() => {
if(!debouncing) return;
// Set timer
const timer = setTimeout(() => {
onDebouncedChange?.(input);
}, debouncingValue);
// Clear timeout
return () => clearTimeout(timer);
}, [input, debouncing, debouncingValue]);
return (
<Input
{...props}
value={isControlled ? props.value : input}
onChange={(e) => {
if(!isControlled) {
setInput(e.target.value);
}
props.onChange?.(e);
}}
className={cn(className)}
/>
)
}
export default DebouncedInput;Usage
import DebouncedInput from "@/components/ui/debounced-input"
export function BasicExample() {
return (
<DebouncedInput
debouncing
debouncingValue={500}
onDebouncedChange={(val) => console.log(val)}
placeholder="Type safely..."
/>
)
}import { useState } from "react"
import DebouncedInput from "@/components/ui/debounced-input"
export function ControlledExample() {
const [value, setValue] = useState("")
return (
<DebouncedInput
value={value}
onChange={(e) => setValue(e.target.value)}
debouncing
onDebouncedChange={(val) => console.log("Final:", val)}
/>
)
}import DebouncedInput from "@/components/ui/debounced-input"
export function SearchExample() {
const handleSearch = (query: string) => {
if (!query) return;
// Perform API call here
console.log(`Searching for: ${query}`);
}
return (
<DebouncedInput
placeholder="Search users..."
debouncing
debouncingValue={300} // Wait 300ms after last keystroke
onDebouncedChange={handleSearch}
/>
)
}Props
The DebouncedInput accepts all props from the standard HTML input element (via Shadcn Input), plus the following:
| Prop | Type | Default | Description |
|---|---|---|---|
debouncing | boolean | false | Enables the debouncing functionality. |
debouncingValue | number | 500 | The delay in milliseconds before the onDebouncedChange callback is triggered. |
onDebouncedChange | (value: string) => void | - | Callback function triggered after the debounce delay. Receives the current input value. |
className | string | - | Additional CSS classes to apply to the input. |
Note
If debouncing is false, onDebouncedChange will not be called. You should use the standard onChange prop in that case, or ensure debouncing is true if you want the delayed callback.
Concepts
What is debouncing ?
Debouncing is a programming practice used to ensure that time-consuming tasks do not fire so often, making them inefficient. In the context of an Input, it limits the rate at which a function fires.
An API call or state update happens on every keystroke.
The action waits until the user stops typing for a set duration.