How to Optimize React Rendering With useDeferredValue and useTransition
React apps can suffer from sluggish rendering and input lag when too many components re-render on state changes. Luckily, React 18 introduced two hooks—useDeferredValue and useTransition—to help us build smoother, more responsive UIs. This post dives into how and when to use each. Why React Rendering Can Lag When state updates trigger re-renders of expensive components, you might notice UI jank or input lag. This is especially true for: Large lists Heavy computations Complex layouts with deep component trees Let’s see how useTransition and useDeferredValue help alleviate that. useTransition: Prioritize UI Responsiveness useTransition allows you to mark some state updates as non-urgent. React will prioritize urgent updates like text input, and defer the expensive ones. import { useState, useTransition } from "react"; function SearchComponent({ items }) { const [query, setQuery] = useState(""); const [filteredItems, setFilteredItems] = useState(items); const [isPending, startTransition] = useTransition(); const handleChange = (e) => { const value = e.target.value; setQuery(value); startTransition(() => { const results = items.filter((item) => item.toLowerCase().includes(value.toLowerCase()) ); setFilteredItems(results); }); }; return ( {isPending && Loading...} {filteredItems.map((item) => ( {item} ))} ); } Key Benefits: Input stays responsive Loading state automatically shown during delay useDeferredValue: Delay Expensive Reads useDeferredValue is ideal when the UI doesn’t need to immediately update with the latest value. You can defer re-rendering of components that depend on rapidly changing values. import { useDeferredValue } from "react"; function DeferredSearchResults({ query, items }) { const deferredQuery = useDeferredValue(query); const filtered = items.filter((item) => item.toLowerCase().includes(deferredQuery.toLowerCase()) ); return ( {filtered.map((item) => ( {item} ))} ); } When to Use Which useTransition: When the state update is expensive and non-urgent useDeferredValue: When consuming components don’t need the newest value immediately Pro Tip: Combine Both You can use both hooks in tandem for truly responsive experiences in heavy UI components, like tables or charts. Conclusion These two hooks are powerful tools for keeping your apps feeling fast—even under load. Introduced in React 18, they give developers fine-grained control over how updates propagate, without major rewrites. If this post helped you, consider buying me a coffee: buymeacoffee.com/hexshift
React apps can suffer from sluggish rendering and input lag when too many components re-render on state changes. Luckily, React 18 introduced two hooks—useDeferredValue
and useTransition
—to help us build smoother, more responsive UIs. This post dives into how and when to use each.
Why React Rendering Can Lag
When state updates trigger re-renders of expensive components, you might notice UI jank or input lag. This is especially true for:
- Large lists
- Heavy computations
- Complex layouts with deep component trees
Let’s see how useTransition
and useDeferredValue
help alleviate that.
useTransition: Prioritize UI Responsiveness
useTransition
allows you to mark some state updates as non-urgent. React will prioritize urgent updates like text input, and defer the expensive ones.
import { useState, useTransition } from "react";
function SearchComponent({ items }) {
const [query, setQuery] = useState("");
const [filteredItems, setFilteredItems] = useState(items);
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
const value = e.target.value;
setQuery(value);
startTransition(() => {
const results = items.filter((item) =>
item.toLowerCase().includes(value.toLowerCase())
);
setFilteredItems(results);
});
};
return (
{isPending && Loading...}
{filteredItems.map((item) => (
- {item}
))}
);
}
Key Benefits:
- Input stays responsive
- Loading state automatically shown during delay
useDeferredValue: Delay Expensive Reads
useDeferredValue
is ideal when the UI doesn’t need to immediately update with the latest value. You can defer re-rendering of components that depend on rapidly changing values.
import { useDeferredValue } from "react";
function DeferredSearchResults({ query, items }) {
const deferredQuery = useDeferredValue(query);
const filtered = items.filter((item) =>
item.toLowerCase().includes(deferredQuery.toLowerCase())
);
return (
{filtered.map((item) => (
- {item}
))}
);
}
When to Use Which
- useTransition: When the state update is expensive and non-urgent
- useDeferredValue: When consuming components don’t need the newest value immediately
Pro Tip: Combine Both
You can use both hooks in tandem for truly responsive experiences in heavy UI components, like tables or charts.
Conclusion
These two hooks are powerful tools for keeping your apps feeling fast—even under load. Introduced in React 18, they give developers fine-grained control over how updates propagate, without major rewrites.
If this post helped you, consider buying me a coffee: buymeacoffee.com/hexshift