Render Props Pattern

Overview

The Render Props pattern is a technique for sharing code between React components using a prop whose value is a function. A component with a render prop takes a function that returns a React element and calls it instead of implementing its own render logic. This pattern provides a powerful way to share stateful logic while keeping components flexible and reusable.

Example

The example below shows a MouseTracker component that tracks the mouse position and shares that data through a render prop. Move your mouse over the area to see it in action.

Move your mouse in this area!

Mouse position: (0, 0)

Alternative Syntax

The same component can also be used with an explicit render prop:

The mouse coordinates are:

x: 0, y: 0

Example

The same component can also be used with an explicit render prop:

Move your mouse inside this box

Current mouse position: (0, 0)

Code Implementation

// MouseTracker.tsx
import React, { useState, ReactNode } from 'react';

// Type for mouse position
type MousePosition = {
  x: number;
  y: number;
};

// Props type for the MouseTracker component
type MouseTrackerProps = {
  render?: (position: MousePosition) => ReactNode;
  children?: (position: MousePosition) => ReactNode;
};

export const MouseTracker: React.FC<MouseTrackerProps> = ({ render, children }) => {
  const [position, setPosition] = useState<MousePosition>({ x: 0, y: 0 });
  
  const handleMouseMove = (event: React.MouseEvent) => {
    const rect = event.currentTarget.getBoundingClientRect();
    const x = event.clientX - rect.left;
    const y = event.clientY - rect.top;
    
    setPosition({ x, y });
  };
  
  return (
    <div onMouseMove={handleMouseMove}>
      {render ? render(position) : children ? children(position) : null}
    </div>
  );
};

// Usage with children function:
// <MouseTracker>
//   {(mousePosition) => (
//     <p>Mouse position: {mousePosition.x}, {mousePosition.y}</p>
//   )}
// </MouseTracker>

// Usage with render prop:
// <MouseTracker 
//   render={(mousePosition) => (
//     <p>Mouse position: {mousePosition.x}, {mousePosition.y}</p>
//   )}
// />

Key Benefits

  • Flexible sharing of stateful logic between components
  • Component composition rather than inheritance
  • Clear separation between the provider of data and how it is rendered
  • Easier testing as logic and rendering can be tested separately
  • Avoids prop drilling and unnecessary component hierarchy
  • Supports multiple patterns (children as function or explicit render prop)