usePrevious
Return the previous value from the prior render — a tiny utility for diffing, change detection, and transitional UI.
usePrevious
usePrevious returns the value from the previous render. It’s useful for:
- Detecting when a value changed (compare current vs. previous)
- Building transitional UI (e.g., animate from previous to next)
- Writing effects that depend on changes rather than the current value alone
On the initial render, usePrevious(value) returns the current value (i.e., previous === current for the first render). If you need to detect “first render,” track it with a ref or mount state.
API
function usePrevious<T>(value: T): T;Prop
Type
Return value:
- The value from the previous render (on first render, equals the current value).
Usage
1) Show previous and current values
import * as React from 'react';
import { usePrevious } from '@loke/ui/use-previous';
export function Counter() {
const [count, setCount] = React.useState(0);
const prev = usePrevious(count);
return (
<div className="flex items-center gap-4">
<div>
<div className="text-sm text-muted-foreground">Previous</div>
<div className="font-mono">{prev}</div>
</div>
<div>
<div className="text-sm text-muted-foreground">Current</div>
<div className="font-mono">{count}</div>
</div>
<button
type="button"
className="rounded border px-3 py-1.5"
onClick={() => setCount((c) => c + 1)}
>
Increment
</button>
</div>
);
}2) Run effects only when value changes
import * as React from 'react';
import { usePrevious } from '@loke/ui/use-previous';
export function Notifier({ value }: { value: string }) {
const prev = usePrevious(value);
React.useEffect(() => {
if (prev !== value) {
// eslint-disable-next-line no-console
console.log('Value changed:', { from: prev, to: value });
}
}, [prev, value]);
return <span className="text-sm">Value: {value}</span>;
}3) Transitional UI (e.g., highlight increases vs. decreases)
import * as React from 'react';
import { usePrevious } from '@loke/ui/use-previous';
export function Trending({ score }: { score: number }) {
const prev = usePrevious(score);
const trend = score === prev ? 'neutral' : score > prev ? 'up' : 'down';
return (
<div
className={[
'rounded px-3 py-1.5 font-mono transition-colors',
trend === 'up' && 'bg-green-50 text-green-700',
trend === 'down' && 'bg-red-50 text-red-700',
trend === 'neutral' && 'bg-muted text-muted-foreground',
]
.filter(Boolean)
.join(' ')}
>
{score} {trend === 'up' ? '↑' : trend === 'down' ? '↓' : '·'}
</div>
);
}Behavior details
- First render: returns the current value (so
previous === current). - Subsequent renders: returns the value from the prior render.
- It does not trigger re-renders by itself; it’s purely derived from a ref + memo logic.
- SSR: Safe to use in server-rendered apps (no direct DOM access).
If you need a sentinel for “no previous value yet,” track a mounted ref:
const mounted = React.useRef(false); React.useEffect(() => { mounted.current = true; }, []);
Tips and patterns
- Comparing objects: for deep comparisons, use a stable serialization or a custom comparator before storing/reading previous values.
- Pairs well with controlled inputs or props to detect external changes.
- Great for analytics (log once when value changes) and animation triggers.
Import
import { usePrevious } from '@loke/ui/use-previous';useSize
Observe and return a DOM element’s width and height using ResizeObserver — updates synchronously via a layout effect and returns undefined until an element is available.
useControllableState
A small hook for building components that support both controlled and uncontrolled usage with one consistent API.