DebounceInput

free

DebounceInput wraps Input with a configurable delay so onChange fires only after the user stops typing.

Preview
Dark

Debounced value:

import { useState } from 'react';
import DebounceInput from '@/components/composites/DebounceInput';

export default function UsageDemo() {
  const [value, setValue] = useState('');

  return (
    <div className="flex flex-col gap-2">
      <DebounceInput
        placeholder="Type something…"
        wait={300}
        onChange={(e) => setValue(e.target.value)}
      />
      <p className="text-sm text-muted-foreground">
        Debounced value: {value || '—'}
      </p>
    </div>
  );
}

Installation

Add this component with the NateUI CLI.

npx nateui@latest add DebounceInput

Examples

Sizes

Preview
Dark
import DebounceInput from '@/components/composites/DebounceInput';

export default function SizesDemo() {
  return (
    <div className="flex flex-col gap-2">
      <DebounceInput size="sm" placeholder="Small" wait={300} />
      <DebounceInput placeholder="Default" wait={300} />
      <DebounceInput size="lg" placeholder="Large" wait={300} />
    </div>
  );
}

Disabled

Preview
Dark
import DebounceInput from '@/components/composites/DebounceInput';

export default function DisabledDemo() {
  return (
    <div className="flex flex-col gap-2">
      <DebounceInput disabled placeholder="Disabled input" wait={300} />
      <DebounceInput
        readOnly
        value="Read-only value"
        placeholder="Disabled input"
        wait={300}
      />
    </div>
  );
}

Search Filter

Preview
Dark
  • Apple
  • Avocado
  • Banana
  • Blueberry
  • Cherry
  • Cranberry
  • Grape
  • Grapefruit
  • Kiwi
  • Lemon
  • Lime
  • Mango
  • Orange
  • Papaya
  • Peach
  • Pineapple
  • Plum
  • Raspberry
  • Strawberry
  • Watermelon
import { useMemo, useState } from 'react';
import DebounceInput from '@/components/composites/DebounceInput';

const FRUITS = [
  'Apple', 'Avocado', 'Banana', 'Blueberry',
  'Cherry', 'Cranberry', 'Grape', 'Grapefruit',
  'Kiwi', 'Lemon', 'Lime', 'Mango',
  'Orange', 'Papaya', 'Peach', 'Pineapple',
  'Plum', 'Raspberry', 'Strawberry', 'Watermelon',
];

export default function SearchFilterDemo() {
  const [query, setQuery] = useState('');

  const results = useMemo(() => {
    if (!query) return FRUITS;
    const lower = query.toLowerCase();
    return FRUITS.filter((f) => f.toLowerCase().includes(lower));
  }, [query]);

  return (
    <div className="flex flex-col gap-2">
      <DebounceInput
        placeholder="Search fruits…"
        wait={200}
        onChange={(e) => setQuery(e.target.value)}
      />
      <ul className="max-h-60 overflow-y-auto border rounded-card divide-y divide-border">
        {results.map((f) => (
          <li key={f} className="p-2">
            {f}
          </li>
        ))}
        {results.length === 0 && (
          <li className="p-2 text-muted-foreground">No results</li>
        )}
      </ul>
    </div>
  );
}

API

PropDescriptionTypeDefault
waitDebounce delay in milliseconds before onChange fires.number500
valueControlled input value.string
placeholderPlaceholder text.string
disabledDisables the input.booleanfalse
sizeLayout size variant.'sm' | 'default' | 'lg''default'
onChangeCalled after the debounce delay with the native change event.(e: ChangeEvent<HTMLInputElement>) => void