Falytom logoFALYTOM
Games
ToolsUI ComponentsServicesProductsAbout
LoginSign Up
menu
  1. Home
  2. /Web Development
  3. /React
  4. /Render Props
Your Progress0%
0 of 42 completed

React Topics

Getting Started

  • What is React?
  • React vs Vanilla JavaScript
  • Setting Up Your Environment
  • Your First React App

JSX Fundamentals

  • Introduction to JSX
  • JSX Expressions and Variables
  • Conditional Rendering
  • Lists and Keys
  • Styling in JSX

Components Basics

  • Function Components
  • Props
  • Children Prop
  • Props Destructuring
  • Component Composition

State Management

  • Introduction to State
  • useState Hook
  • State Updates and Re-renders
  • Multiple State Variables
  • State Best Practices

Events and Interactivity

  • Handling Events
  • Event Objects
  • Forms in React
  • Form Validation

Side Effects and Data

  • Introduction to useEffect
  • Data Fetching
  • Cleanup Functions
  • Dependency Arrays

Advanced Hooks

  • useRef Hook
  • useContext Hook
  • useMemo Hook
  • useCallback Hook

React Patterns

  • Custom Hooks
  • Higher-Order Components
  • Render Props Pattern

React Router

  • Introduction to React Router
  • Routes and Navigation
  • Dynamic Routes and Parameters

Best Practices

  • Component Organization
  • Performance Optimization
  • Error Handling
  • Testing React Components

Real World Project

  • Building a Complete App

Render Props Pattern

Sharing code using render props

The render props pattern is another way to share logic between components. Instead of wrapping components (like HOCs), you pass a function as a prop that returns JSX. The component calls this function with data, letting you control what gets rendered. This pattern is flexible and powerful, though like HOCs, it's been largely replaced by hooks in modern React. Let's learn this classic pattern! šŸŽØ

What Are Render Props?

A render prop is a function prop that a component uses to know what to render:

render-prop-basic.jsx
// Component with render prop
function MouseTracker({ render }) {
  const [position, setPosition] = React.useState({ x: 0, y: 0 })
  
  React.useEffect(() => {
    const handleMouseMove = (e) => {
      setPosition({ x: e.clientX, y: e.clientY })
    }
    
    window.addEventListener('mousemove', handleMouseMove)
    return () => window.removeEventListener('mousemove', handleMouseMove)
  }, [])
  
  // Call render function with data
  return render(position)
}

// Usage: Pass function that returns JSX
function App() {
  return (
    <MouseTracker 
      render={(position) => (
        <div>
          Mouse is at: {position.x}, {position.y}
        </div>
      )}
    />
  )
}

// MouseTracker handles logic, you control rendering!

Render Props Definition:

A render prop is:
• A function passed as a prop
• Returns React elements (JSX)
• Receives data from the component
• Lets you control what's rendered

Pattern: Component provides data → Function returns JSX

Why Use Render Props?

The Problem: Different UIs, Same Logic

problem-duplication.jsx
// āŒ Component 1: Mouse position as coordinates
function MouseCoordinates() {
  const [position, setPosition] = React.useState({ x: 0, y: 0 })
  
  React.useEffect(() => {
    const handleMouseMove = (e) => {
      setPosition({ x: e.clientX, y: e.clientY })
    }
    window.addEventListener('mousemove', handleMouseMove)
    return () => window.removeEventListener('mousemove', handleMouseMove)
  }, [])
  
  return <p>Mouse: {position.x}, {position.y}</p>
}

// āŒ Component 2: Mouse position as dot
function MouseDot() {
  const [position, setPosition] = React.useState({ x: 0, y: 0 })
  
  React.useEffect(() => {
    const handleMouseMove = (e) => {
      setPosition({ x: e.clientX, y: e.clientY })
    }
    window.addEventListener('mousemove', handleMouseMove)
    return () => window.removeEventListener('mousemove', handleMouseMove)
  }, [])
  
  return (
    <div 
      style={{ 
        position: 'absolute', 
        left: position.x, 
        top: position.y,
        width: 10,
        height: 10,
        background: 'red'
      }} 
    />
  )
}

// Problem: Same mouse tracking logic, duplicated!

The Solution: Extract Logic with Render Props

solution-render-props.jsx
// āœ… Component that handles mouse tracking
function Mouse({ render }) {
  const [position, setPosition] = React.useState({ x: 0, y: 0 })
  
  React.useEffect(() => {
    const handleMouseMove = (e) => {
      setPosition({ x: e.clientX, y: e.clientY })
    }
    window.addEventListener('mousemove', handleMouseMove)
    return () => window.removeEventListener('mousemove', handleMouseMove)
  }, [])
  
  return render(position)
}

// āœ… Usage 1: Render as coordinates
function App() {
  return (
    <Mouse 
      render={(pos) => (
        <p>Mouse: {pos.x}, {pos.y}</p>
      )}
    />
  )
}

// āœ… Usage 2: Render as dot
function App() {
  return (
    <Mouse 
      render={(pos) => (
        <div 
          style={{ 
            position: 'absolute', 
            left: pos.x, 
            top: pos.y,
            width: 10,
            height: 10,
            background: 'red'
          }} 
        />
      )}
    />
  )
}

// Logic written once, rendered many ways!

Children as Function (Function as Children)

Instead of a prop named "render", you can use the children prop:

children-as-function.jsx
// Component using children as function
function Mouse({ children }) {
  const [position, setPosition] = React.useState({ x: 0, y: 0 })
  
  React.useEffect(() => {
    const handleMouseMove = (e) => {
      setPosition({ x: e.clientX, y: e.clientY })
    }
    window.addEventListener('mousemove', handleMouseMove)
    return () => window.removeEventListener('mousemove', handleMouseMove)
  }, [])
  
  // Call children as function
  return children(position)
}

// Usage: Pass function as children
function App() {
  return (
    <Mouse>
      {(position) => (
        <div>
          <h1>Move your mouse!</h1>
          <p>Position: {position.x}, {position.y}</p>
        </div>
      )}
    </Mouse>
  )
}

// This is called "function as children" or "children as a function"

šŸ’” Render Prop vs Children

Both patterns work the same way:

Render Prop:

Children as Function:
<Mouse>{(pos) => <div>...</div>}</Mouse>

Children as function is often more readable!

Common Render Props Patterns

1. Data Fetching

pattern-fetch.jsx
function DataFetcher({ url, children }) {
  const [data, setData] = React.useState(null)
  const [loading, setLoading] = React.useState(true)
  const [error, setError] = React.useState(null)
  
  React.useEffect(() => {
    fetch(url)
      .then(res => res.json())
      .then(data => {
        setData(data)
        setLoading(false)
      })
      .catch(err => {
        setError(err.message)
        setLoading(false)
      })
  }, [url])
  
  return children({ data, loading, error })
}

// Usage:
function App() {
  return (
    <DataFetcher url="/api/users">
      {({ data, loading, error }) => {
        if (loading) return <div>Loading...</div>
        if (error) return <div>Error: {error}</div>
        
        return (
          <ul>
            {data.map(user => (
              <li key={user.id}>{user.name}</li>
            ))}
          </ul>
        )
      }}
    </DataFetcher>
  )
}

2. Toggle State

pattern-toggle.jsx
function Toggle({ children, initial = false }) {
  const [on, setOn] = React.useState(initial)
  
  const toggle = () => setOn(!on)
  const setTrue = () => setOn(true)
  const setFalse = () => setOn(false)
  
  return children({ on, toggle, setTrue, setFalse })
}

// Usage:
function App() {
  return (
    <Toggle initial={false}>
      {({ on, toggle, setTrue, setFalse }) => (
        <div>
          <p>Status: {on ? 'On' : 'Off'}</p>
          <button onClick={toggle}>Toggle</button>
          <button onClick={setTrue}>Turn On</button>
          <button onClick={setFalse}>Turn Off</button>
        </div>
      )}
    </Toggle>
  )
}

3. Window Dimensions

pattern-window.jsx
function WindowSize({ children }) {
  const [size, setSize] = React.useState({
    width: window.innerWidth,
    height: window.innerHeight
  })
  
  React.useEffect(() => {
    const handleResize = () => {
      setSize({
        width: window.innerWidth,
        height: window.innerHeight
      })
    }
    
    window.addEventListener('resize', handleResize)
    return () => window.removeEventListener('resize', handleResize)
  }, [])
  
  return children(size)
}

// Usage:
function App() {
  return (
    <WindowSize>
      {({ width, height }) => (
        <div>
          <p>Window size: {width} x {height}</p>
          {width < 768 ? (
            <p>Mobile view</p>
          ) : (
            <p>Desktop view</p>
          )}
        </div>
      )}
    </WindowSize>
  )
}

4. Form State Management

pattern-form.jsx
function Form({ children, initialValues, onSubmit }) {
  const [values, setValues] = React.useState(initialValues)
  const [errors, setErrors] = React.useState({})
  
  const handleChange = (name) => (e) => {
    setValues({ ...values, [name]: e.target.value })
  }
  
  const handleSubmit = (e) => {
    e.preventDefault()
    onSubmit(values)
  }
  
  const validate = (name, rule) => {
    if (rule && !rule(values[name])) {
      setErrors({ ...errors, [name]: 'Invalid' })
    } else {
      const newErrors = { ...errors }
      delete newErrors[name]
      setErrors(newErrors)
    }
  }
  
  return children({
    values,
    errors,
    handleChange,
    handleSubmit,
    validate
  })
}

// Usage:
function App() {
  return (
    <Form 
      initialValues={{ email: '', password: '' }}
      onSubmit={(values) => console.log(values)}
    >
      {({ values, errors, handleChange, handleSubmit }) => (
        <form onSubmit={handleSubmit}>
          <input 
            value={values.email}
            onChange={handleChange('email')}
            placeholder="Email"
          />
          {errors.email && <span>{errors.email}</span>}
          
          <input 
            type="password"
            value={values.password}
            onChange={handleChange('password')}
            placeholder="Password"
          />
          
          <button type="submit">Submit</button>
        </form>
      )}
    </Form>
  )
}

Comparing the Three Patterns

Same Logic, Three Approaches:

comparison-all.jsx
// 1. CUSTOM HOOK (Modern, Preferred)
function useMouse() {
  const [position, setPosition] = React.useState({ x: 0, y: 0 })
  
  React.useEffect(() => {
    const handleMouseMove = (e) => {
      setPosition({ x: e.clientX, y: e.clientY })
    }
    window.addEventListener('mousemove', handleMouseMove)
    return () => window.removeEventListener('mousemove', handleMouseMove)
  }, [])
  
  return position
}

// Usage:
function MyComponent() {
  const position = useMouse()
  return <div>Mouse: {position.x}, {position.y}</div>
}


// 2. RENDER PROPS
function Mouse({ children }) {
  const [position, setPosition] = React.useState({ x: 0, y: 0 })
  
  React.useEffect(() => {
    const handleMouseMove = (e) => {
      setPosition({ x: e.clientX, y: e.clientY })
    }
    window.addEventListener('mousemove', handleMouseMove)
    return () => window.removeEventListener('mousemove', handleMouseMove)
  }, [])
  
  return children(position)
}

// Usage:
function MyComponent() {
  return (
    <Mouse>
      {(position) => <div>Mouse: {position.x}, {position.y}</div>}
    </Mouse>
  )
}


// 3. HOC
function withMouse(Component) {
  return function WithMouse(props) {
    const [position, setPosition] = React.useState({ x: 0, y: 0 })
    
    React.useEffect(() => {
      const handleMouseMove = (e) => {
        setPosition({ x: e.clientX, y: e.clientY })
      }
      window.addEventListener('mousemove', handleMouseMove)
      return () => window.removeEventListener('mousemove', handleMouseMove)
    }, [])
    
    return <Component {...props} position={position} />
  }
}

// Usage:
const MyComponentWithMouse = withMouse(MyComponent)


// COMPARISON:
// Custom Hook: Simplest, most flexible, best for new code
// Render Props: Flexible UI control, good for complex rendering logic
// HOC: Good for enhancing components, but less flexible

Pros and Cons:

pros-cons.jsx
// CUSTOM HOOKS
āœ… Simplest to use
āœ… Most flexible
āœ… Can return multiple values
āœ… No extra nesting
āœ… Best TypeScript support
āŒ Only for function components

// RENDER PROPS
āœ… Very flexible UI control
āœ… Works with class components
āœ… Clear data flow
āŒ Creates nesting (wrapper hell)
āŒ Verbose syntax
āŒ Can impact performance

// HOC
āœ… Works with class components
āœ… Compose multiple HOCs
āŒ Props might collide
āŒ Wrapped component hidden in DevTools
āŒ Most complex pattern
āŒ Static methods need copying

Render Props Best Practices

  • Use children as function - More readable than render prop
  • Memoize render functions - Prevent unnecessary re-renders
  • Keep logic in component - Render function should only render
  • Name parameters clearly - Destructure for clarity
  • Provide TypeScript types - Type the function signature
  • Consider hooks first - Simpler for new code
  • Avoid deep nesting - Multiple render props create "wrapper hell"
  • Document what's provided - Clear prop descriptions

When to Use Render Props:

Consider render props when:
• You need very flexible UI control
• Multiple ways to render same data
• Working with class components
• Building a component library

Prefer custom hooks when:
• Writing new code
• Using function components
• Want simplicity
• Don't need JSX control

Common Render Props Mistakes

1. Creating Function in Render

mistake-inline.jsx
// āŒ Wrong - Creates new function every render
function App() {
  return (
    <Mouse>
      {(position) => (  // New function every time!
        <div>{position.x}, {position.y}</div>
      )}
    </Mouse>
  )
}

// This causes Mouse to re-render even if position didn't change

// āœ… Better - Memoize with useCallback
function App() {
  const renderMouse = React.useCallback(
    (position) => <div>{position.x}, {position.y}</div>,
    []
  )
  
  return <Mouse>{renderMouse}</Mouse>
}

// āœ… Or extract to separate component
function MouseDisplay({ position }) {
  return <div>{position.x}, {position.y}</div>
}

function App() {
  return (
    <Mouse>
      {(position) => <MouseDisplay position={position} />}
    </Mouse>
  )
}

2. Wrapper Hell

mistake-nesting.jsx
// āŒ Wrong - Too much nesting!
function App() {
  return (
    <DataFetcher url="/api/user">
      {({ data: user, loading: userLoading }) => (
        <DataFetcher url="/api/posts">
          {({ data: posts, loading: postsLoading }) => (
            <WindowSize>
              {({ width }) => (
                <Theme>
                  {({ theme }) => (
                    // Finally render something!
                    <div style={{ color: theme.text }}>
                      {userLoading || postsLoading ? 'Loading...' : 
                        width < 768 ? 'Mobile' : 'Desktop'
                      }
                    </div>
                  )}
                </Theme>
              )}
            </WindowSize>
          )}
        </DataFetcher>
      )}
    </DataFetcher>
  )
}

// āœ… Better - Use custom hooks!
function App() {
  const { data: user, loading: userLoading } = useFetch('/api/user')
  const { data: posts, loading: postsLoading } = useFetch('/api/posts')
  const { width } = useWindowSize()
  const { theme } = useTheme()
  
  return (
    <div style={{ color: theme.text }}>
      {userLoading || postsLoading ? 'Loading...' : 
        width < 768 ? 'Mobile' : 'Desktop'
      }
    </div>
  )
}

// Much cleaner!

3. Not Forwarding Props

mistake-props.jsx
// āŒ Wrong - Props lost
function Container({ children, ...props }) {
  return (
    <div>
      {children({ data: 'some data' })}
      {/* props not used! */}
    </div>
  )
}

// āœ… Correct - Forward props
function Container({ children, className, ...props }) {
  return (
    <div className={className} {...props}>
      {children({ data: 'some data' })}
    </div>
  )
}

Key Takeaways

  • Render props are functions that return JSX
  • Pattern: Component provides data → Function returns UI
  • Children as function is cleaner than render prop
  • Great for flexible UI control
  • Component handles logic, you control rendering
  • Can pass any data through the function
  • Avoids HOC complexity
  • Works with both class and function components
  • Can lead to "wrapper hell" with multiple nesting
  • Modern React prefers custom hooks
  • Still useful for component libraries
  • Memoize render functions for performance
  • Clear, explicit data flow

What's Next?

Congratulations! You've completed the React Patterns section. You now understand all three classic patterns:

  • Custom Hooks - Modern, preferred approach
  • Higher-Order Components - Component enhancement
  • Render Props - Flexible rendering control

You have a complete toolkit for code reuse in React! You know when to use each pattern and their tradeoffs. For new code, start with custom hooks - they're simpler and more flexible. But understanding HOCs and render props helps you work with existing codebases and libraries.

You've now completed 34 out of 42 tutorials - over 80% of the React tutorial series! You have strong knowledge of:

  • JSX and component fundamentals
  • State management and hooks
  • Side effects and data fetching
  • Performance optimization
  • Advanced patterns and code reuse

Keep building - you're almost a React expert! šŸŽ‰

Learning how to share code using the render props pattern in React!

Previous
Higher-Order Components
Next
Introduction to React Router

Never Miss a New React Tutorial

Join 2,000+ developers learning React step-by-step. Get new tutorials, tips, and exclusive resources delivered to your inbox - completely FREE.

You might also like

UI Components

Ready-made components for your projects

Developer Tools

Boost productivity with handy generators

HTML Game

Learn HTML concepts through interactive gameplay

Join Our Web Development Training Program

Master frontend & backend development with hands-on projects.

React Tutorials

0 of 42 completed

Your Progress0%

Topics

Getting Started

  • What is React?
  • React vs Vanilla JavaScript
  • Setting Up Your Environment
  • Your First React App

JSX Fundamentals

  • Introduction to JSX
  • JSX Expressions and Variables
  • Conditional Rendering
  • Lists and Keys
  • Styling in JSX

Components Basics

  • Function Components
  • Props
  • Children Prop
  • Props Destructuring
  • Component Composition

State Management

  • Introduction to State
  • useState Hook
  • State Updates and Re-renders
  • Multiple State Variables
  • State Best Practices

Events and Interactivity

  • Handling Events
  • Event Objects
  • Forms in React
  • Form Validation

Side Effects and Data

  • Introduction to useEffect
  • Data Fetching
  • Cleanup Functions
  • Dependency Arrays

Advanced Hooks

  • useRef Hook
  • useContext Hook
  • useMemo Hook
  • useCallback Hook

React Patterns

  • Custom Hooks
  • Higher-Order Components
  • Render Props Pattern

React Router

  • Introduction to React Router
  • Routes and Navigation
  • Dynamic Routes and Parameters

Best Practices

  • Component Organization
  • Performance Optimization
  • Error Handling
  • Testing React Components

Real World Project

  • Building a Complete App
Falytom logoFALYTOM

Learn technologies like HTML, CSS, JavaScript, React, NodeJS and more through interactive games, tutorials, components and free tools. Transform your businesses with AI chatbots, SEO-optimized websites, and professional development services.

Ā© 2025 Tomilola Group
  • Facebook logo
  • Twitter logo
  • Instagram logo
  • LinkedIn logo
  • GitHub logo
  • YouTube logo