React useDeferredValue: Optimize Search Without Debounce

Problem

A search filter over a large list causes janky typing because every keystroke triggers a full re-render. Debounce helps, but introduces a fixed delay and hides the input value while typing.

function SearchList({ items }) {
  const [query, setQuery] = useState('');

  // With 10,000 items, filtering runs on every keystroke
  const filtered = items.filter(item =>
    item.name.toLowerCase().includes(query.toLowerCase())
  );

  return (
    <>
      <input value={query} onChange={e => setQuery(e.target.value)} />
      <List items={filtered} />
    </>
  );
}

Solution

useDeferredValue keeps the input instantly responsive while deferring the expensive rendering.

import { useState, useDeferredValue, memo } from 'react';

function SearchList({ items }) {
  const [query, setQuery] = useState('');
  const deferredQuery = useDeferredValue(query);

  // Input reflects query immediately, list uses deferredQuery
  const filtered = items.filter(item =>
    item.name.toLowerCase().includes(deferredQuery.toLowerCase())
  );

  const isStale = query !== deferredQuery;

  return (
    <>
      <input value={query} onChange={e => setQuery(e.target.value)} />
      <div style=>
        <SlowList items={filtered} />
      </div>
    </>
  );
}

// Must wrap with memo for the deferral to actually work
const SlowList = memo(function SlowList({ items }) {
  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
});

The difference from debounce:

debounce: typing stops → wait 300ms → start filtering
useDeferredValue: input updates instantly → filtering updates when browser is idle

User input is never delayed. React automatically defers only the expensive re-render.

Key Points

  • useDeferredValue separates urgent updates (input) from non-urgent updates (list rendering)
  • Must be used with memo — without it, the component re-renders every time regardless
  • Unlike debounce, there’s no fixed delay — fast devices see near-instant updates, slow devices get graceful degradation