Render Props Pattern in React JS
Render Props Pattern is a React design pattern used to share reusable logic between components through a function passed as a prop. The function returns JSX that determines what should be displayed on the screen.
This pattern separates the logic of a component from its visual presentation. One component manages behavior or data, while another decides how that data should be rendered.
Course Links: JustAcademy React JS Training Course | Register for React JS Course Demo
1. What Is the Render Props Pattern?
In the Render Props Pattern, a component accepts a function as a prop and calls that function with data or state. The function returns the React elements that should be rendered.
A render prop is not a special React API. It is an ordinary function prop used to customize a component's rendered output.
Basic Example
function Greeting({ render }) {
return (
<div>
{render("Manish")}
</div>
);
}
function App() {
return (
<Greeting
render={(name) => <h1>Hello, {name}!</h1>}
/>
);
}
export default App;
Output
Hello, Manish!
Explanation:
Greeting receives a function through its render prop.
- The component calls
render("Manish").
- The function returns an
h1 element.
- React renders the returned element on the screen.
2. Why Do We Use Render Props?
- Logic reuse: Share behavior across multiple components without duplicating implementation.
- Flexible UI: Let the parent decide how the shared data should appear.
- Separation of concerns: Keep behavior and presentation in separate places.
- Custom rendering: Display the same data in different formats.
- Reusable components: Build components that can work with different visual layouts.
- Dynamic interfaces: Pass state or calculated values to a rendering function.
3. How Does the Render Props Pattern Work?
- Create a component that manages reusable logic or data.
- Define a function prop such as
render, renderItem, or renderContent.
- Call the function and pass the required data or state to it.
- Return the function's result from the component's JSX.
- Provide a rendering function from the parent component.
- Use the received values to customize the rendered UI.
Basic Flow
Parent Component
|
| Passes render function
v
Reusable Logic Component
|
| Supplies data or state
v
Render Function
|
| Returns JSX
v
React UI
4. Understanding the Render Prop
The render prop can have any suitable name. The name render is common, but other names such as renderContent and renderItem are also valid.
function UserInfo({ renderContent }) {
const user = {
name: "Rahul",
role: "Developer"
};
return renderContent(user);
}
function App() {
return (
<UserInfo
renderContent={(user) => (
<div>
<h2>{user.name}</h2>
<p>Role: {user.role}</p>
</div>
)}
/>
);
}
Here, UserInfo supplies the user data, while the parent decides how the user's name and role should be displayed.
5. Render Props Using the children Function
The Render Props Pattern can also use the special children prop as a function. This approach is often called a function-as-a-child pattern.
Example
function Counter({ children }) {
const [count, setCount] = React.useState(0);
return (
<div>
{children({
count,
increment: () => setCount((current) => current + 1)
})}
</div>
);
}
function App() {
return (
<Counter>
{({ count, increment }) => (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
)}
</Counter>
);
}
Important: In this example, React must be available in the file, or import useState and use it directly. A modern version using a direct import is shown below.
import { useState } from "react";
function Counter({ children }) {
const [count, setCount] = useState(0);
return children({
count,
increment: () => setCount((current) => current + 1)
});
}
export default function App() {
return (
<Counter>
{({ count, increment }) => (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
)}
</Counter>
);
}
In the second example, the counter manages its state and exposes the count and increment function to its child rendering function. The parent decides the appearance of the counter UI.
6. Practical Example: Mouse Position Tracker
A common use case for Render Props is sharing mouse-position logic while allowing different components to display the coordinates in different ways.
Step 1: Create MouseTracker.jsx
import { useState } from "react";
function MouseTracker({ render }) {
const [position, setPosition] = useState({
x: 0,
y: 0
});
function handleMouseMove(event) {
setPosition({
x: event.clientX,
y: event.clientY
});
}
return (
<div
onMouseMove={handleMouseMove}
style={{
height: "250px",
border: "1px solid #999",
padding: "20px"
}}
>
{render(position)}
</div>
);
}
export default MouseTracker;
Step 2: Use MouseTracker.jsx
import MouseTracker from "./MouseTracker";
function App() {
return (
<div>
<h1>Mouse Position Tracker</h1>
<MouseTracker
render={({ x, y }) => (
<h2>X: {x}, Y: {y}</h2>
)}
/>
</div>
);
}
export default App;
How It Works
MouseTracker tracks the mouse coordinates using state.
- The
onMouseMove event updates the coordinates.
- The
render function receives the latest position.
- The parent chooses how to display the coordinates.
This pattern allows the same mouse-tracking behavior to be reused for a text display, a tooltip, or another visual component without rewriting the tracking logic.
7. Practical Example: Reusable Data List
A reusable list component can accept a rendering function so that the same collection of data can be displayed as cards, rows, or simple list items.
DataList.jsx
function DataList({ items, renderItem }) {
return (
<ul>
{items.map((item) => (
<li key={item.id}>
{renderItem(item)}
</li>
))}
</ul>
);
}
export default DataList;
App.jsx
import DataList from "./DataList";
const students = [
{ id: 1, name: "Amit", course: "React JS" },
{ id: 2, name: "Priya", course: "JavaScript" },
{ id: 3, name: "Neha", course: "HTML and CSS" }
];
function App() {
return (
<div>
<h1>Students</h1>
<DataList
items={students}
renderItem={(student) => (
<div>
<strong>{student.name}</strong>
{" - "}
<span>{student.course}</span>
</div>
)}
/>
</div>
);
}
export default App;
The DataList component handles list iteration, while the renderItem function controls the presentation of each student.
8. Render Props for Conditional Rendering
A render function can receive status information and decide which UI to display.
function StatusMessage({ isLoading, error, renderSuccess }) {
if (isLoading) {
return <p>Loading...</p>;
}
if (error) {
return <p role="alert">Something went wrong.</p>;
}
return renderSuccess();
}
function App() {
return (
<StatusMessage
isLoading={false}
error={null}
renderSuccess={() => (
<h2>Data loaded successfully!</h2>
)}
/>
);
}
This example demonstrates UI customization through a function prop. In a real data-fetching component, loading and error states would typically be managed by the component or supplied from a data source.
9. Render Props vs. Higher-Order Components
Render Props and Higher-Order Components (HOCs) are both patterns for sharing reusable behavior. They differ in how that behavior is connected to the UI.
| Feature |
Render Props |
Higher-Order Components |
| Definition |
Uses a function prop to determine rendered UI. |
A function takes a component and returns an enhanced component. |
| Usage |
Pass a function such as render or renderItem. |
Wrap a component using an HOC function. |
| Customization |
The caller directly controls the returned JSX. |
The HOC usually injects props or adds behavior. |
| Composition |
Logic and presentation are composed in JSX. |
Components are enhanced through wrapping. |
| Potential issue |
Nested render functions may make JSX difficult to read. |
Many wrappers can make component relationships harder to trace. |
| Example |
|
withMouse(Component) |
10. Render Props vs. Custom Hooks
Custom Hooks are often a simpler alternative when the main goal is to reuse stateful logic rather than customize a child component's rendered output.
| Feature |
Render Props |
Custom Hooks |
| Logic reuse |
Shared through a component and a function prop. |
Shared through a reusable Hook. |
| Rendering |
The caller supplies a rendering function. |
The component uses returned values to render its own JSX. |
| Component tree |
Adds a component to the tree. |
Does not add a component by itself. |
| Code structure |
Can create nested functions and JSX. |
Often simpler for straightforward logic reuse. |
| Best use case |
When a component should expose behavior and let the caller choose the UI. |
When multiple components need the same stateful logic. |
Custom Hook Example
import { useState } from "react";
function useCounter() {
const [count, setCount] = useState(0);
function increment() {
setCount((current) => current + 1);
}
return { count, increment };
}
function Counter() {
const { count, increment } = useCounter();
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
A custom Hook shares the counter logic without requiring a render-prop component. Choose the pattern that produces the clearest API for your use case.
11. Advantages of the Render Props Pattern
- Reusable logic: Common behavior can be implemented once and reused.
- Flexible presentation: Different parents can render the same data differently.
- Clear data sharing: The render function receives exactly the values it needs.
- Component composition: It works naturally with React props and JSX.
- Less duplicated behavior: Event handling and state management can stay in a reusable component.
- Customizable UI: The reusable component does not need to know the exact visual structure.
12. Disadvantages and Limitations
- Nested JSX: Multiple render props can make the component tree harder to read.
- Extra component: The pattern often adds a component to the React tree.
- Function identity: Inline render functions are new function objects on each parent render, which can affect shallow memoization.
- Learning curve: Beginners may need time to understand why a function returns JSX.
- Potential overuse: Simple components may not need this pattern.
- Alternative patterns: Custom Hooks or ordinary composition may be simpler for many modern use cases.
13. Common Mistakes and Solutions
| Mistake |
Solution |
| Forgetting to call the render function. |
Invoke it and return the resulting React node. |
| Passing JSX instead of a function when the component expects a function. |
Pass a function such as render={(data) => {data} }. |
| Calling a missing render prop. |
Make the prop required or provide a safe default. |
| Passing the wrong data to the function. |
Document the function's parameters and supply the expected values. |
| Using array indexes as keys unnecessarily. |
Prefer stable IDs from the data when rendering lists. |
| Using render props for every simple UI component. |
Prefer ordinary composition or a custom Hook when it is clearer. |
| Adding side effects inside the render function. |
Keep rendering pure and move side effects into event handlers or appropriate Effects. |
14. Performance Considerations
- React may re-render components when their state, props, or context change.
- An inline render function usually receives a new function identity on each parent render.
- That new identity can prevent shallow memoization from skipping a render.
- Use
useCallback only when stable function identity provides a real benefit and its dependencies are correct.
- Keep render functions pure; they should describe UI rather than perform side effects.
- Profile the application before adding performance optimizations.
Example Using useCallback
import { useCallback } from "react";
function App() {
const renderMessage = useCallback((name) => {
return <p>Welcome, {name}!</p>;
}, []);
return <Greeting render={renderMessage} />;
}
function Greeting({ render }) {
return render("Manish");
}
export default App;
useCallback can preserve the function reference between renders when its dependencies have not changed. It is not required for correctness, and it does not automatically prevent every re-render.
15. Testing Components That Use Render Props
Test the rendered behavior rather than only checking that a function was passed. React Testing Library and Vitest can verify the visible output and user interactions.
Example Component
function Message({ render }) {
return <div>{render("React")}</div>;
}
export default Message;
Example Test
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import Message from "./Message";
describe("Message", () => {
it("renders content returned by the render prop", () => {
render(
<Message
render={(name) => <h1>Hello, {name}!</h1>}
/>
);
expect(
screen.getByRole("heading", { name: "Hello, React!" })
).toBeTruthy();
});
});
This test assumes React Testing Library and Vitest are configured. It verifies that the component displays the JSX returned by the render prop.
16. Real-World Use Cases
- Mouse tracking: Share pointer coordinates with different visual components.
- Data lists: Render the same collection as cards, rows, or compact list items.
- Search components: Share search behavior while allowing a custom results display.
- Form controls: Expose validation state and let callers customize messages.
- Permission checks: Provide authorization information to a custom rendering function.
- Loading states: Expose loading and error information while allowing custom status UI.
- Pagination: Share page state and let the caller design the pagination controls.
17. Practical Project: Searchable Student List
Build a reusable student list component that provides filtering logic and lets the parent choose how matching students appear.
Requirements
- Create a component that manages a search query.
- Accept an array of student objects.
- Filter students by name based on the query.
- Pass the query and filtered results to a render function.
- Allow the parent to display the results as cards or a list.
- Show a friendly message when no students match.
- Test filtering and empty-result behavior.
Suggested Component Structure
StudentSearch/
├── StudentSearch.jsx
├── App.jsx
├── StudentSearch.css
└── StudentSearch.test.jsx
Expected Learning Outcomes
- Understand how to pass functions as props.
- Separate filtering logic from presentation.
- Pass state and derived data into a rendering function.
- Build reusable components with customizable output.
18. Interview Questions and Answers
Q1. What is the Render Props Pattern?
It is a React design pattern in which a component receives a function prop and calls it to determine what UI should be rendered.
Q2. Is render a special React prop?
No. It is an ordinary function prop. Other names, such as renderItem and renderContent, can also be used.
Q3. What is the purpose of Render Props?
It allows components to share behavior or data while letting callers customize the visual output.
Q4. Can children be used as a render function?
Yes. A component can receive a function as its children prop and call it with data or state.
Q5. What is the difference between Render Props and HOCs?
Render Props customize output through a function passed into a component. HOCs accept a component and return an enhanced component.
Q6. What is the difference between Render Props and custom Hooks?
Render Props expose behavior through a component and a rendering function. Custom Hooks share stateful logic directly between function components.
Q7. Can a render prop receive multiple values?
Yes. It can receive individual arguments or one object containing multiple values and functions.
Q8. Does a render prop have to return JSX?
It must return a valid React node when used as rendered content. This may be an element, string, number, array of nodes, or an empty node such as null.
Q9. Does Render Props improve performance automatically?
No. It is a code reuse and composition pattern, not an automatic performance optimization.
Q10. What is a common limitation of Render Props?
Deeply nested render functions can make JSX difficult to read, and inline functions can affect shallow memoization.
Q11. When should Render Props be used?
Use them when a reusable component should expose behavior or data while allowing the caller to control the rendered output.
Q12. Are Render Props still useful in modern React?
Yes, but custom Hooks and ordinary component composition often provide simpler alternatives for sharing logic. Render Props remain useful when customized rendering is central to the component API.
19. Practice Exercises
- Create a Greeting component that accepts a
render prop.
- Build a counter that shares its count and increment function through a render prop.
- Create a MouseTracker that displays coordinates in different formats.
- Build a reusable list component with a
renderItem prop.
- Create a searchable list with custom result rendering.
- Compare the Render Props approach with a custom Hook for the same feature.
- Write tests to verify the output and interaction behavior.
20. Quick Revision
- Render Props is a design pattern for sharing behavior and customizing UI.
- A render prop is a function passed to a component.
- The component calls the function and supplies data or state.
- The function returns the UI to display.
- The function can be named
render, renderItem, or something else.
- A function passed as
children is another form of this pattern.
- Render Props can be compared with HOCs and custom Hooks.
- Use the pattern when it makes component reuse and presentation clearer.
- Avoid unnecessary nesting and optimize only when needed.
21. Official Learning Resources
22. Conclusion
The Render Props Pattern helps React developers reuse behavior while keeping the rendered interface customizable. By passing a function as a prop, a component can expose data, state, and actions without deciding exactly how every caller must display them.
Although custom Hooks and normal component composition are often simpler for modern React applications, Render Props remain a useful technique for reusable components that need a flexible rendering API.
Learn More: JustAcademy React JS Training Course | Register for React JS Course Demo