Skip to content
C Codeloom
React

React Render Props vs Hooks

Compare render props and hooks as two ways to share reusable logic in React, with examples, mental models, and guidance on which pattern fits modern codebases.

·4 min read · By Codeloom
Intermediate 9 min read

What you'll learn

  • What render props solved
  • How hooks supersede most uses
  • When render props still shine
  • How to migrate between them
  • Performance and readability tradeoffs

Prerequisites

  • Familiar with React components

Render props and hooks are two answers to the same problem: how do you reuse stateful logic across components? This post compares them honestly, shows the same logic in both forms, and explains where each still belongs.

What and Why

Before hooks, React had no first-class way to share logic. Mixins were dead, and inheritance never fit. Render props filled the gap by letting a component own state and hand it to children through a function prop. The child decides how to render the data.

Hooks landed in React 16.8 and changed everything. A custom hook is just a function that calls other hooks. It returns values directly instead of via JSX, which removes nesting and ceremony. For most logic sharing today, hooks are the default and render props are the legacy choice.

Still, render props are not dead. They remain a clean way to share behavior that involves rendering, not just data.

Mental Model

A render prop says: I will manage the state, you tell me what to draw with it. A hook says: I will manage the state, you do whatever you want with the values I return. The first inverts control through JSX, the second through plain function returns.

Hooks compose linearly. You call them top to bottom in a function. Render props compose by nesting JSX, which creates pyramid shapes when you stack more than one.

Hands-on Example

Consider tracking the mouse position. Here it is as a render prop and as a hook.

render prop                    hook
<Mouse>                        const pos = useMouse();
{(pos) => (                  return <Pointer pos={pos} />;
  <Pointer pos={pos} />
)}
</Mouse>
Render props nest, hooks compose linearly
// Render prop version
class Mouse extends React.Component {
  state = { x: 0, y: 0 };
  handleMove = (e) => this.setState({ x: e.clientX, y: e.clientY });
  render() {
    return (
      <div onMouseMove={this.handleMove}>
        {this.props.children(this.state)}
      </div>
    );
  }
}

// Usage
<Mouse>{(pos) => <p>{pos.x}, {pos.y}</p>}</Mouse>

// Hook version
function useMouse() {
  const [pos, setPos] = useState({ x: 0, y: 0 });
  useEffect(() => {
    const onMove = (e) => setPos({ x: e.clientX, y: e.clientY });
    window.addEventListener('mousemove', onMove);
    return () => window.removeEventListener('mousemove', onMove);
  }, []);
  return pos;
}

// Usage
function Pointer() {
  const pos = useMouse();
  return <p>{pos.x}, {pos.y}</p>;
}

The hook version is shorter, easier to test, and composes with other hooks without nesting. The render prop version is more verbose but still readable.

Common Pitfalls

With render props, the deepest pitfall is performance. The function child is recreated on every render, breaking memoization for whoever consumes it. You also get pyramid nesting once you compose two or three render props together.

With hooks, the rules of hooks trip new developers up. You cannot call them conditionally or inside loops. Closures over stale state inside useEffect and useCallback lead to bugs that look like the framework misbehaving.

Mixing both in the same component without reason makes code hard to read. Pick one style per concern.

Best Practices

For new code, default to custom hooks. Prefix with use, return tuples or objects, and keep them focused. A hook that returns ten unrelated things is a smell.

Reach for render props when the pattern is genuinely about rendering, not data. Headless component libraries like Downshift and Radix often expose render props because the consumer truly controls markup.

If you maintain old code, migrate render props to hooks incrementally. Wrap the render-prop component in a thin hook adapter and update consumers one at a time.

Wrap-up

Hooks won the logic-sharing debate for good reason. They are shorter, more composable, and easier to test. Render props still have a place in headless libraries and rendering-focused APIs. Use the right tool for the job, but in 2026, the default is a hook.