Skip to main content
Back to articles

Make Your React App Feel Faster with One Simple Change

Reactperformancefrontend
Make Your React App Feel Faster with One Simple Change

React apps often get “slow” not because the backend is slow, but because the UI is doing extra work on every render. The easiest win I’ve seen (especially in component-heavy pages) is stabilizing props so memoized children can actually skip re-renders.

In this article, we’ll fix one common culprit: inline objects/functions passed as props.

The Bottleneck: Unstable Props

Even if the values “look the same”, React sees new references:

// ❌ Unstable: new references on every render
<ExpensiveList
  items={items}
  options={{ dense: true }}
  onSelect={(id) => setSelectedId(id)}
/>;

If ExpensiveList is wrapped with React.memo, it still re-renders because options and onSelect are new every time.

The One Simple Change: useMemo + useCallback

Make those props stable:

import { useCallback, useMemo, useState } from "react";
 
export function Page({ items }: { items: Array<{ id: string }> }) {
  const [selectedId, setSelectedId] = useState<string | null>(null);
 
  const options = useMemo(() => ({ dense: true }), []);
  const onSelect = useCallback((id: string) => setSelectedId(id), []);
 
  return (
    <ExpensiveList items={items} options={options} onSelect={onSelect} />
  );
}

When This Helps (and When It Doesn’t)

  • Helps when you have memoized children (React.memo, memo, useMemo outputs) and you’re accidentally invalidating memoization with unstable props.
  • Doesn’t help if the bottleneck is heavy computations inside the component or too much work in effects—then you need profiling and larger refactors.

Quick Checklist

  • Avoid inline {} and [] in props when you expect memoization to work.
  • Avoid inline arrow functions in props for memoized children (unless you’re fine with re-renders).
  • Prefer useCallback/useMemo only where it prevents real work—don’t sprinkle it everywhere.

See you in the next one.

Related Articles