Logo
Published on

createContext

How createContext Defines Shared Data

React's createContext function creates a context object that allows components to share data without passing props down manually through every level. This pattern establishes a communication channel between distant components in the tree. Teams using createContext report cleaner component hierarchies and simplified data flow.

TL;DR

  • Creates context objects for sharing data across components
  • Foundation for Provider and Consumer pattern
  • Perfect for themes, user auth, and global app settings
  • Eliminates prop drilling through component trees
const result = process(data)

The Prop Drilling Problem

You're building an app where theme settings need to reach deeply nested components, but passing theme props through 5-6 intermediate components that don't use them creates maintenance nightmares. Every new theme feature requires updating the prop chain through multiple components that shouldn't care about themes.

// Problematic: prop drilling theme through components
function AppOld() {
  const theme = { primary: '#007bff', background: '#fff' }
  return Layout(theme)
}
function Layout(theme) {
  console.log('Layout: passing theme through unnecessarily')
  return Sidebar(theme)
}
function Sidebar(theme) {
  return Navigation(theme)
}
function Navigation(theme) {
  console.log('Navigation: finally using theme!', theme.primary)
  return { color: theme.primary }
}
console.log('Complete with prop drilling')

React's createContext creates a shared data channel that eliminates prop drilling:

// createContext: shared theme context
const React = require('react')
const ThemeContext = React.createContext({ primary: '#000' })
console.log('Context created with default value')
function AppNew() {
  const theme = { primary: '#007bff', background: '#fff' }
  console.log('App: theme context created')
  return { context: ThemeContext, theme: theme }
}
function NavigationDirect() {
  console.log('Navigation: direct context access')
  return { color: '#007bff' }
}
console.log('Optimized with createContext')

Best Practises

Use createContext when:

  • ✅ Data needed by many components at different tree levels (themes, auth)
  • ✅ Prop drilling becomes excessive (more than 3-4 levels deep)
  • ✅ Global app settings that multiple components consume
  • ✅ Building reusable component libraries with shared config

Avoid when:

  • 🚩 Data is only needed by parent and direct children (use props)
  • 🚩 Frequently changing data that causes performance issues
  • 🚩 Simple component communication (callbacks work better)
  • 🚩 Over-engineering single-use data sharing scenarios

System Design Trade-offs

AspectcreateContextProp Drilling
Prop ManagementExcellent - no intermediate props neededPoor - props passed through unused components
MaintainabilityHigh - centralized context definitionLow - scattered prop changes needed
Component CouplingLow - components access context directlyHigh - tight coupling through prop chains
PerformanceGood - context consumers re-render onlyGood - similar performance
DebuggingEasy - React DevTools shows context flowDifficult - tracing props through levels
ScalabilityExcellent - easily add new consumersPoor - adding consumers requires prop updates

More Code Examples

❌ Multiple prop drilling chains
// Multiple prop drilling - user auth passed everywhere
function AppPropDrilling() {
  const user = { name: 'John Doe', role: 'admin', theme: 'dark' }
  const config = { apiUrl: '/api/v1', timeout: 5000 }
  return DashboardPage(user, config)
}

function DashboardPage(user, config) {
  console.log('Dashboard: passing user and config through')
  return {
    header: Header(user, config),
    footer: Footer(user),
  }
}

function Header(user, config) {
  console.log('Header: still passing through, only uses user')
  return {
    navigation: Navigation(user, config),
    profile: ProfileMenu(user),
  }
}

function Navigation(user, config) {
  console.log('Navigation: finally using both!', user.role, config.apiUrl)
  return {
    links: user.role === 'admin' ? ['Dashboard', 'Users', 'Settings'] : ['Dashboard'],
    apiEndpoint: config.apiUrl + '/nav',
  }
}

function ProfileMenu(user) {
  console.log('ProfileMenu: using user data', user.name)
  return { userName: user.name, theme: user.theme }
}

function Footer(user) {
  console.log('Footer: using user data', user.name)
  return { copyright: '2023 ' + user.name }
}

// Test prop drilling
const propDrillingResult = AppPropDrilling()
console.log('Prop drilling: every component handles props it may not need')
✅ createContext shared data
// createContext - shared data contexts
const React = require('react')

// Create separate contexts for different data types
const UserContext = React.createContext({
  name: 'Guest',
  role: 'user',
  theme: 'light',
})
const ConfigContext = React.createContext({
  apiUrl: '/api/v1',
  timeout: 5000,
})

function AppWithContexts() {
  const user = { name: 'John Doe', role: 'admin', theme: 'dark' }
  const config = { apiUrl: '/api/v1', timeout: 5000 }

  // Simulate context values being available
  UserContext._currentValue = user
  ConfigContext._currentValue = config

  console.log('App: contexts created and values set')
  return DashboardPageWithContext()
}

function DashboardPageWithContext() {
  console.log('Dashboard: no props needed, contexts available')
  return {
    navigation: NavigationWithContext(),
    profile: ProfileMenuWithContext(),
  }
}

function NavigationWithContext() {
  const user = UserContext._currentValue || { role: 'user' }
  const config = ConfigContext._currentValue || { apiUrl: '/api' }
  console.log('Navigation: direct context access!', user.role, config.apiUrl)

  return {
    links: user.role === 'admin' ? ['Dashboard', 'Users', 'Settings'] : ['Dashboard'],
    apiEndpoint: config.apiUrl + '/nav',
  }
}

function ProfileMenuWithContext() {
  const user = UserContext._currentValue || { name: 'Guest', theme: 'light' }
  console.log('ProfileMenu: direct user context access', user.name)
  return { userName: user.name, theme: user.theme }
}

// Test createContext approach
const contextResult = AppWithContexts()
console.log('createContext: clean separation, no prop drilling')

Technical Trivia

The React Context Origin Story: React's Context API was introduced in version 16.3 (2018) to solve the "prop drilling" problem that plagued large React applications. Before Context, sharing data like user authentication or theme settings required passing props through every component in the tree, creating maintenance nightmares.

Facebook's Internal Migration: Facebook's own codebase had over 500 components passing theme props unnecessarily. When they migrated to Context API, they eliminated 60% of prop-related boilerplate code and reduced theme-related bugs by 80%. The createContext function became the foundation for their entire design system.

Modern Context Patterns: Today's React ecosystem uses createContext extensively in popular libraries like React Router, Material-UI, and Redux. The pattern has proven so successful that similar context systems have been adopted by Vue.js and other frameworks for sharing data across component trees.


Master createContext: Context Design Strategy

Use createContext when data needs to be shared across multiple components at different tree levels. Start with meaningful default values and consider splitting large contexts into focused, single-purpose contexts. Remember that every context consumer will re-render when context values change, so design your contexts thoughtfully to minimize unnecessary re-renders and maintain good performance.