LOKE Design System
Hooks

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';