# useDebouncedValue

A hook that defers a rapidly changing value until it settles.

## Installation

```bash
npx shadcn@latest add https://mwui.vercel.app/r/use-debounced-value.json
```

[Registry JSON](https://mwui.vercel.app/r/use-debounced-value.json)

## Preview

```tsx
import * as React from "react";

import { Input } from "@/components/ui/input";

import { useDebouncedValue } from "@/hooks/use-debounced-value";

export function Preview() {
  const [query, setQuery] = React.useState("");
  const debouncedQuery = useDebouncedValue(query, 500);
  const isPending = query !== debouncedQuery;

  return (
    <div className="flex w-full max-w-xs flex-col gap-3">
      <Input
        value={query}
        placeholder="Type here…"
        aria-label="Search"
        onChange={(event) => setQuery(event.target.value)}
      />

      <div className="flex flex-col gap-1 text-sm">
        <p className="text-muted-foreground">
          Live: <span className="font-mono text-foreground">{query || "—"}</span>
        </p>
        <p className="text-muted-foreground">
          Debounced: <span className="font-mono text-foreground">{debouncedQuery || "—"}</span>
        </p>
        <p className="text-xs text-muted-foreground">
          {isPending ? "Waiting for input to settle…" : "Settled."}
        </p>
      </div>
    </div>
  );
}
```


## Source

### hooks/use-debounced-value.ts

```ts
"use client";

import * as React from "react";

function useDebouncedValue<T>(value: T, delay = 300): T {
  const [debounced, setDebounced] = React.useState(value);

  React.useEffect(() => {
    if (delay <= 0) {
      setDebounced(value);
      return undefined;
    }

    const timeout = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(timeout);
  }, [value, delay]);

  return debounced;
}

export { useDebouncedValue };
```



## Usage

Returns a copy of the value that only updates once it has stopped changing for `delay`
milliseconds. Use it to keep an input responsive while throttling whatever the input drives —
a search request, an expensive filter, a URL update.

```tsx
import { useDebouncedValue } from "@/hooks/use-debounced-value";

function Search() {
  const [query, setQuery] = React.useState("");
  const debouncedQuery = useDebouncedValue(query, 300);

  React.useEffect(() => {
    void search(debouncedQuery);
  }, [debouncedQuery]);

  return <input value={query} onChange={(event) => setQuery(event.target.value)} />;
}
```

The input stays bound to `query`, so typing never feels laggy. Only `debouncedQuery` is throttled.

## Knowing when it is pending

The hook deliberately returns just the value. Compare it against the source to derive the
pending state, rather than tracking a second piece of state:

```tsx
const isPending = query !== debouncedQuery;
```

## Notes

- The timer resets on every change and is cleared on unmount, so a value that never settles never
  fires.
- A `delay` of `0` or less updates synchronously on the next effect, which is useful for turning
  debouncing off behind a flag.
- Works with any value type. Non-primitive values are compared by identity, so memoize objects
  before passing them in.

