An interactive comparison of the three ways to reuse stateful logic in React — render props, HOCs, and custom hooks — with the same mouse-tracker logic shown all three ways and the "wrapper hell" that hooks flatten.
▶ Live: https://render-props-vs-hooks.pages.dev/
Move your mouse and all three readouts update with the exact same logic. The difference is entirely in how that logic is packaged and consumed.
Custom hook — share a function:
function useMouse() {
const [pos, setPos] = useState({ x: 0, y: 0 });
useEffect(() => {
const h = (e) => setPos({ x: e.clientX, y: e.clientY });
window.addEventListener("mousemove", h);
return () => window.removeEventListener("mousemove", h);
}, []);
return pos;
}
const { x, y } = useMouse(); // no wrapperRender prop — share a component (function-as-child):
<MouseTracker>
{({ x, y }) => <p>{x}, {y}</p>}
</MouseTracker>HOC — wrap and inject a prop:
const DotWithMouse = withMouse(Dot); // injects a `mouse` propRender props and HOCs both wrap your component in another component. Stack a few shared concerns and you get a pyramid of nesting and callbacks:
<MouseTracker>{mouse => (
<WindowSize>{size => (
<Toggle>{[on, tog] => (
<Auth>{user => <Dashboard /> }</Auth>
)}</Toggle>
)}</WindowSize>
)}</MouseTracker>Hooks compose flat — one line each, and a hook can call other hooks:
const mouse = useMouse();
const size = useWindowSize();
const [on, toggle] = useToggle();
const user = useAuth();That's the whole reason hooks replaced both patterns for logic reuse.
For sharing stateful logic — yes, hooks win and it's not close. But render props aren't gone; they moved to a different job: injecting what to render. When a component owns behaviour but lets the caller decide the markup — a renderItem/renderRow prop or a function-as-child — that's still the clean tool (virtualized lists, data tables, tooltips, headless UI). Rule of thumb: hooks for logic, render props for "you tell me what to render." HOCs are mostly legacy — reach for a hook first.
React 19 · TypeScript · Vite.
npm install
npm run devMIT © 2026 dev48v — dev48v.infy.uk