Skip to main content
Practice Problems

Render props pattern in React

What is the Render Props Pattern?

Render props is a pattern where a component receives a function as a prop that returns React elements. This allows the component to share its internal logic/state while letting the consumer control the rendering.


Basic Example

tsx
// Component with render prop — shares mouse position function MouseTracker({ render }: { render: (position: { x: number; y: number }) => React.ReactNode; }) { const [position, setPosition] = useState({ x: 0, y: 0 }); const handleMouseMove = (e: React.MouseEvent) => { setPosition({ x: e.clientX, y: e.clientY }); }; return ( <div onMouseMove={handleMouseMove} style={{ height: "100vh" }}> {render(position)} </div> ); } // Consumer controls the UI function App() { return ( <MouseTracker render={({ x, y }) => ( <p>Mouse is at ({x}, {y})</p> )} /> ); }

Using children as Render Prop

tsx
function MouseTracker({ children }: { children: (position: { x: number; y: number }) => React.ReactNode; }) { const [position, setPosition] = useState({ x: 0, y: 0 }); return ( <div onMouseMove={e => setPosition({ x: e.clientX, y: e.clientY })}> {children(position)} </div> ); } // Usage — cleaner syntax <MouseTracker> {({ x, y }) => <span>({x}, {y})</span>} </MouseTracker>

Practical Examples

Toggle Component

tsx
function Toggle({ children }: { children: (props: { isOn: boolean; toggle: () => void }) => React.ReactNode; }) { const [isOn, setIsOn] = useState(false); const toggle = () => setIsOn(prev => !prev); return <>{children({ isOn, toggle })}</>; } // Reusable toggle logic, different UIs <Toggle> {({ isOn, toggle }) => ( <button onClick={toggle}> {isOn ? "ON" : "OFF"} </button> )} </Toggle> <Toggle> {({ isOn, toggle }) => ( <div className={`switch ${isOn ? "active" : ""}`} onClick={toggle} /> )} </Toggle>

Data Fetcher

tsx
function DataFetcher<T>({ url, children }: { url: string; children: (props: { data: T | null; loading: boolean; error: string | null }) => React.ReactNode; }) { const [data, setData] = useState<T | null>(null); const [loading, setLoading] = useState(true); const [error, setError] = useState<string | null>(null); useEffect(() => { fetch(url) .then(res => res.json()) .then(setData) .catch(e => setError(e.message)) .finally(() => setLoading(false)); }, [url]); return <>{children({ data, loading, error })}</>; } // Usage <DataFetcher<User[]> url="/api/users"> {({ data, loading, error }) => { if (loading) return <Spinner />; if (error) return <Error message={error} />; return <UserList users={data!} />; }} </DataFetcher>

Render Props vs Custom Hooks

FeatureRender PropsCustom Hooks
Reuse logic
No wrapper component❌ (adds wrapper)
Works in class components
Can control rendering
ReadabilityCan become nestedCleaner
tsx
// The same logic as a Custom Hook (modern approach) function useMousePosition() { const [position, setPosition] = useState({ x: 0, y: 0 }); useEffect(() => { const handler = (e: MouseEvent) => setPosition({ x: e.clientX, y: e.clientY }); window.addEventListener("mousemove", handler); return () => window.removeEventListener("mousemove", handler); }, []); return position; } // Usage — cleaner, no wrapper function App() { const { x, y } = useMousePosition(); return <p>({x}, {y})</p>; }

Important:

Render props is a powerful pattern for sharing stateful logic between components. While custom hooks have largely replaced render props in modern React, the pattern is still valuable in libraries (e.g., React Router, Formik, Downshift) and when you need components that work with both class and functional components.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems