Is a React Hook
used to share data globally across the component tree without manually passing props through every level(called as props drilling).
Call useContext at the top level of your component
What is Context?
Context is a way to share data across many components easily
Without passing it step-by-step using props
Problem without Context
Normally in React:
Data goes like this (parent → child → child → child)
App → Form → Panel → Button
If Button needs data, you must pass props through all:
<App theme="dark">
<Form theme="dark">
<Panel theme="dark">
<Button theme="dark" />
</Panel>
</Form>
</App>
This is called prop drilling (messy and repetitive)
Solution: Context
With Context, you give data once at the top
<ThemeContext value="dark">
<App />
</ThemeContext>
Then any component can directly use it:
const theme = useContext(ThemeContext);
use cases
Theme (dark / light)
Language (English / Tamil)
Logged-in user data
Settings
Why use Context?
Avoids passing props again and again
Makes code clean
Easy to manage shared data
summary
Props → pass data step-by-step
Context → share data directly
Useful when many components need same data
When to Use Context
Use Context when many components need the same data
Especially when the data is global (used everywhere)
Problem without Context
You pass data like this:
App → Header → Layout → Button
To send theme to Button:
<App theme="dark">
<Header theme="dark">
<Layout theme="dark">
<Button theme="dark" />
</Layout>
</Header>
</App>
This is repetitive and messy (prop drilling)
Solution using Context
Provide once at the top:
<ThemeContext value="dark">
<App />
</ThemeContext>
Use anywhere directly:
const theme = useContext(ThemeContext);
When to use Context
Use Context if:
Same data is needed in many components
Passing props becomes annoying
When NOT to use Context
Small/local data
Data used in only 1–2 components
In that case → use props
summary
Context = for global shared data
Avoids prop drilling
Use for theme, user, language
Don’t overuse it
const value = useContext(SomeContext)
Parameters
- Context is like a container type (not actual data)
- It just defines: “What kind of data can be shared”
- Context does NOT store data by itself Provider stores and provide data to components.
- It only helps to pass data between components
example
Context = empty box label (name only)
Provider = puts data inside the box
useContext =takes data from the box
How it works
- Create context
- const ThemeContext = createContext();
- Provide value
<ThemeContext.Provider value="dark">
- Use value
- const theme = useContext(ThemeContext);
- Context itself = just a reference / type
- Actual data comes from Provider
- SomeContext = created using createContext()
- It does NOT store data
- It only defines what data can be shared
- Data is given using Provider
- Data is used using useContext()
Returns
What does useContext() return?
It gives you the current value of the context
From where does it get the value?
It looks upwards in the component tree
Finds the nearest Provider
<SomeContext.Provider value="data">
Then returns that value
If no Provider is found?
It returns the default value you gave:
createContext("default value");
The value is always latest (up-to-date)
If the context value changes:
React automatically updates (re-renders) the component
example
const ThemeContext = createContext("light");
function MyComponent() {
const theme = useContext(ThemeContext);
return <h1>{theme}</h1>;
}
What happens?
If Provider gives "dark" → shows dark
If no Provider → shows light (default)
If value changes → UI updates automatically
summary
useContext() = get shared data
Takes value from nearest Provider
If no Provider → uses default value
Always updated → React re-renders automatically
Caveats
1. Provider must be above the component
useContext() only works if the Provider is above it
Wrong:
function MyComponent() {
const value = useContext(MyContext);
return (
<MyContext.Provider value="data">
<h1>{value}</h1>
</MyContext.Provider>
);
}
This won’t work properly because Provider is inside, not above.
Correct:
<MyContext.Provider value="data">
<MyComponent />
</MyContext.Provider>
Now useContext() can read the value
2. React updates automatically
When context value changes:
All components using that context will re-render (update)
React checks old vs new value using Object.is (just compares values)
Even if you use React.memo(),It cannot stop updates from context
Context updates will always reach children
3. Duplicate context problem
If your project accidentally creates two copies of the same context, it breaks
Example problem:
One file uses MyContext
Another file uses a different copy of MyContext
Even if names are same, React sees them as different objects
Context must be:
Same context object (=== equal)
Why this happens?
Sometimes due to:
wrong imports
duplicate packages
symlinks
summary
Provider must be above component
Context changes → UI updates automatically
memo cannot stop context updates
Use same context object, not duplicates
Usage
It explains how to send data from top component → deep inside components
Without passing props again and again
This is called “passing data deeply” using Context
Main idea
You use:
Provider → to give data
useContext() → to read data
Step-by-step
1. Create context
const ThemeContext = createContext(null);
2. Provide value at top
<ThemeContext value="dark">
<Form />
</ThemeContext>
“All components inside can use 'dark' theme”
3. Use it anywhere inside
const theme = useContext(ThemeContext);
Now theme = "dark"
It doesn’t matter how deep the component is
Example:
App
└── Form
└── Panel
└── Button
Even if Button is very deep, it still gets "dark"
No need to pass props like this
<Form theme="dark" />
<Panel theme="dark" />
<Button theme="dark" />
useContext() only looks upwards
Check parent components
NOT check inside itself or below
Wrong
“If I create Provider inside same component, it will work” → NO
Correct
Provider must be above the component using it
summary
Context = share data across many components
Provider = gives value at top
useContext() = reads value anywhere below
Works even for deeply nested components
Always looks upwards, not down
useContext() always looks for the closest provider above the component that calls it. It searches upwards and does not consider providers in the component from which you’re calling useContext().
Updating data passed via context
Earlier, context was fixed (static)
Now we want it to change over time
Solution: Use useState + Context together
Store data in state
Pass that state into context
Step-by-step
1. Create state in parent
const [theme, setTheme] = useState("light");
theme = current value
setTheme = update function
2. Pass state into context
<ThemeContext value={theme}>
Now all child components can use this value
3. Update the state
setTheme("dark");
This changes the context value
What happens after update?
theme changes
Context value updates
All components using useContext()
Automatically re-render (update UI)
Inside components
const theme = useContext(ThemeContext);
They receive updated value automatically
value="dark" (fixed string)
value={theme} (dynamic value)
{} means JavaScript variable in React
You can control many components from one place
summary
Use state to store changing data
Pass state into context
Update using setState
All components update automatically
Specifying a fallback default value
What is “fallback default value”?
“What value should React use if no Provider is found?”
Example
const ThemeContext = createContext("light");
Default value = "light"
When is this used?
If a component uses:
useContext(ThemeContext);
AND there is no Provider above it
Then React gives:
"light" (default value)
Default value:
Used only when no Provider exists
Does NOT change automatically
Why use a default value?
createContext(null);
createContext("light");
Key difference
Case Value used
Provider exists Uses state value
No Provider Uses default value
summary
createContext("light") → sets default value
If no Provider → default is used
If Provider exists → its value is used
Default value does not change
Use meaningful defaults (not null)
Overriding context for a part of the tree
Changing the context value only for a specific part of your app
You can use another Provider inside to change the value
Example
<ThemeContext value="dark">
...all components here use "dark"
<ThemeContext value="light">
...only this part uses "light"
</ThemeContext>
</ThemeContext>
React always uses the closest Provider
Top level
<ThemeContext value="dark">
Default for whole app = dark
Inside Form
<Button>Sign up</Button>
<Button>Log in</Button>
These buttons use:
"dark"
Overriding in Footer
<ThemeContext value="light">
<Footer />
</ThemeContext>
Now inside Footer:
"light"
Footer button
<Button>Settings</Button>
This button uses:
"light"
Why?
Because it uses the nearest (inner) Provider, not the outer one
Simple visualization
App (dark)
├── Button → dark
├── Button → dark
└── Footer (light)
└── Button → light
Why is this useful?
Change theme for one section only
Customize UI parts differently
Reuse components with different styles
summary
You can nest Providers
Inner Provider overrides outer value
Components use the closest Provider
Helps customize specific parts of UI
Optimizing re-renders when passing objects and functions
What is the problem?
You are passing this in context:
<AuthContext value={{ currentUser, login }}>
This is an object with a function inside
Problem
Every time your component re-renders:
A new object is created
A new function is created
Even if data didn’t change!
Result
React thinks:
“Oh! Context value changed!”
So it re-renders ALL components using this context
Even when not needed
Why is this bad?
In small apps → OK
In big apps → slows performance
Solution
useCallback() → for function
useMemo() → for object
Step 1: Fix function
const login = useCallback((response) => {
storeCredentials(response.credentials);
setCurrentUser(response.user);
}, []);
Now function is not recreated every time
Step 2: Fix object
const contextValue = useMemo(() => ({
currentUser,
login
}), [currentUser, login]);
Now object is not recreated unless data changes
Step 3: Use it
What happens now?
If currentUser doesn’t change:
No new object
No unnecessary re-render
Components update only when needed
summary
Passing objects/functions → causes extra re-renders
Use useCallback() → fix function
Use useMemo() → fix object
Improves performance in large apps
Troubleshooting
Problem 1: Component not getting context value
Your component is not seeing the value from Provider.
Possible reasons:
1. Provider is in the wrong place
You wrote Provider inside or below the component
function MyComponent() {
const value = useContext(MyContext);
return (
<MyContext value="data">
<h1>{value}</h1>
</MyContext>
);
}
Fix
Provider must be above the component
<MyContext value="data">
<MyComponent />
</MyContext>
2. Forgot to wrap with Provider
You didn’t use Provider at all
<MyComponent />
So it only gets default value
3. Provider is in a different place
Maybe you wrapped wrong component
Or structure is incorrect
Use React DevTools to check component tree
4. Duplicate context
Same context created/imported twice
React sees them as different objects
MyContext1 !== MyContext2
So data won’t pass
Fix
Make sure both:
Provider
useContext
use the same context file
Problem 2: Getting undefined always
Even though default value exists
1. Missing value prop
<ThemeContext>
<Button />
</ThemeContext>
Same as:
value={undefined}
2. Wrong prop name
<ThemeContext theme="dark">
Wrong → should be value
Fix
<ThemeContext value="dark">
<Button />
</ThemeContext>
Important
Default value works only if NO Provider exists
createContext("light");
But if Provider exists like this:
<ThemeContext value={undefined}>
Then result = undefined
NOT "light"
summary
Provider must be above component
Always use value prop
Don’t forget to wrap components
Use same context (no duplicates)
Default value works only if no Provider exists
Before You Use Context
Context is useful when many components need the same data
But don’t use it everywhere
Why not use Context always?
Because it can make components:
Harder to reuse
More dependent on global data
Use Context:
When data is used in many places
At different levels (deep nesting)
Don’t use Context:
For small or simple cases
Instead of Context, you can use component composition
What is component composition?
Instead of passing data through many layers
You pass components directly
Problem example (without composition)
Page → Layout → Navigation → Link → Avatar
You pass props like:
<Page user="Priya" avatarSize={50}>
Then every level passes it down ...wrong
Better way (composition)
Pass component directly:
<Page
userLink={<Link user="Priya" />}
avatar={<Avatar size={50} />}
/>
Now no need to pass props through every level
Simple comparison
Method When to use
Props Small/simple data
Composition Avoid prop drilling (simple case)
Context Large/global shared data
summary
Context = for global data used everywhere
Use it carefully (not always)
Composition can be a simpler solution
Don’t overcomplicate small problems
What is the problem?
You are passing props like:
user
avatarSize
Through many components:
Page → Layout → Header → Link → Avatar
But only Avatar actually needs it
repetitive
annoying
hard to maintain
Problem gets worse
If Avatar needs more data later:
You must update all intermediate components
Solution 1 (without Context): Component Composition
Instead of passing data, pass the component itself
<Page
avatar={<Avatar user="Priya" size={50} />}
/>
Only Page knows about user and size
Middle components don’t care
What is this called?
This is called “Inversion of Control”
Top component controls everything
Lower components just render what they receive
Advantages
Less prop passing
Disadvantages
Top component becomes more complex
Child components must be more flexible
You can pass:
One child
Multiple children
Different “slots” (like header, footer, sidebar)
Use composition when:
Only a few components need the data
You just want to avoid prop drilling
When to use Context instead?
Use Context when:
Many components need same data
At different levels
comparison
Method Use case
Props Simple data
Composition Avoid prop drilling (small cases)
Context Global shared data
summary
Passing props deeply = messy
Composition = pass components instead
Context = best for global data
Choose based on complexity
React.createContext
What is React.createContext()?
It is used to create a context
const MyContext = React.createContext(defaultValue);
You are creating a shared data channel.So multiple components can use the same data
What is defaultValue?
It is a backup value
Used only when:
No Provider is found above the component
What value will it return?
Situation Value returned
Provider exists Uses Provider value
No Provider Uses defaultValue
<ThemeContext value={undefined}>
It returns undefined
It will NOT use "light" (default value)
Why defaultValue is useful?
Prevents errors
Helps in testing components
Works even without Provider
summary
createContext() → creates shared data
defaultValue → fallback value
Used only if no Provider exists
undefined from Provider overrides default
Context.Provider
What is MyContext.Provider?
It is used to send (provide) data to other components
<MyContext.Provider value="some value">
Provider = gives data
Components below = receive data
How it works
Any component inside this:
<MyContext.Provider value="dark">
<App />
</MyContext.Provider>
Can access "dark" using:
useContext(MyContext);
- One Provider → many components Many components can use the same value
- You can nest Providers
<MyContext.Provider value="dark">
<MyContext.Provider value="light">
<Footer />
</MyContext.Provider>
</MyContext.Provider>
Inner one overrides outer value
- When value changes If Provider value changes:
value="dark" → value="light"
All components using that context
Automatically re-render (update)
-
React always updates consumers
Even if parent component doesn’t update
Context consumers will still update- How React checks change? It compares: Old value vs New value Using Object.is (simple comparison)
If you pass object/function like this:
value={{ user, login }}
It creates a new object every time
React thinks value changed → causes extra re-renders
summary
Provider = sends data
value = data to share
All child components can use it
Value change → all consumers update
Nested Providers → override values
Be careful with objects (may cause extra updates)
Class.contextType
What is contextType?
It is used in class components to access context
Since hooks (useContext) don’t work in class components, we use this instead
How to use it?
class MyComponent extends React.Component {
static contextType = MyContext;
render() {
return <h1>{this.context}</h1>;
}
}
contextType = MyContext
connects the class to the context
this.context
gives the current value
Where can you use it?
You can use this.context in:
render()
lifecycle methods (like componentDidMount)
limitation
You can use only ONE context
Not allowed:
static contextType = ThemeContext;
static contextType = UserContext;
If you need multiple contexts?
Use other methods like:
<Context.Consumer>
or convert to functional component + useContext()
Alternative syntax (modern)
class MyComponent extends React.Component {
static contextType = MyContext;
}
summary
contextType = used in class components
Lets you access context using this.context
Works in render & lifecycle methods
Only supports one context
Context.Consumer
What is this about?
It is talking about Context.Consumer
Another way to use context (instead of useContext)
What does it do?
It reads (gets) context value
And updates when value changes
How to use it?
<MyContext.Consumer>
{(value) => {
return <h1>{value}</h1>;
}}
</MyContext.Consumer>
Consumer = gets data
It uses a function as a child
That function:
receives the context value
returns UI
Where does value come from?
It checks:
Closest Provider above
If not found → uses default value
Example
<ThemeContext.Provider value="dark">
<ThemeContext.Consumer>
{(theme) => <h1>{theme}</h1>}
</ThemeContext.Consumer>
</ThemeContext.Provider>
Output: dark
If no Provider?
createContext("light");
Output: light
What is “function as a child”?
Instead of normal JSX, you pass a function:
{(value) => <h1>{value}</h1>}
This is called render props pattern
When to use this?
Mostly used:
In older React code
When not using hooks
useContext() is easier and preferred
summary
Consumer = reads context value
Uses a function to return UI
Gets value from nearest Provider
If no Provider → uses default
Mostly replaced by useContext()
Context.displayName
What is Context.displayName?
It is just a name you give to your context
Example
const ThemeContext = createContext();
ThemeContext.displayName = "ThemeContext";
Why is it useful?
Easier to debug
Better readability in DevTools
Helps in large projects
summary
displayName = name for your context
Used only in React DevTools
Makes debugging easier
No effect on functionality
What is “Dynamic Context”?
Earlier, context had fixed values (like "dark")
Context value can change over time
This is called dynamic context
How do we make it dynamic?
Use state (useState) + context
const [theme, setTheme] = useState("light");
Pass it to context:
<ThemeContext value={theme}>
When setTheme() runs → value updates → UI updates
Updating from deep components
A deeply nested component wants to change context
Pass a function through context
Example
- Provide value + function
const [theme, setTheme] = useState("light");
<ThemeContext value={{ theme, setTheme }}>
<App />
</ThemeContext>
- Use it in deep component
const { theme, setTheme } = useContext(ThemeContext);
- Update from inside
<button onClick={() => setTheme("dark")}>
Change Theme
</button>
Context can:
store data
also provide functions to update data
summary
Dynamic context = context that changes
Use useState to store value
Pass both value + update function
Any component can update it
Consuming Multiple Contexts
Using more than one context in the same component
Example:
Theme context
User context
React keeps each context separate
So updates are fast and efficient
Use useContext() multiple times:
const theme = useContext(ThemeContext);
const user = useContext(UserContext);
Older way (Consumer nesting)
<ThemeContext.Consumer>
{theme => (
<UserContext.Consumer>
{user => (
<h1>{theme} - {user.name}</h1>
)}
</UserContext.Consumer>
)}
</ThemeContext.Consumer>
Works, but messy
Create a custom wrapper component
function CombinedContext({ children }) {
const theme = useContext(ThemeContext);
const user = useContext(UserContext);
return children({ theme, user });
}
<CombinedContext>
{({ theme, user }) => (
<h1>{theme} - {user.name}</h1>
)}
</CombinedContext>
summary
You can use multiple contexts in one component
Use useContext() multiple times (best way)
Avoid messy nested Consumers
Create wrapper if used together often
Caveats
Context checks changes using reference (memory) comparison
If you pass an object, React checks:
Is this the same object or a new object?
<AuthContext value={{ user: currentUser }}>
Every time component re-renders:
A new object {} is created
Even if currentUser didn’t change
What React thinks
old object !== new object
All components using this context:
Re-render again and again
Even when data is same
Causes:
Unnecessary renders
Slower performance (especially in big apps)
Solution
Store the value in state (or stable variable)
Correct approach
const [value, setValue] = useState({
user: currentUser
});
Then pass:
<AuthContext value={value}>
Object is stable (same reference)
React won’t re-render unnecessarily
Use useMemo
const contextValue = useMemo(() => ({
user: currentUser
}), [currentUser]);
New object is created only when user changes
Without fix:
Every time → new box → React refreshes everything
With fix:
Same box reused → React stays efficient
summary
Context checks object reference, not just value
New object = React thinks value changed
Causes extra re-renders
Fix using:
useState or
useMemo
What are React Hooks?
state (memory)
context (shared data)
lifecycle features
Normally, data is passed using props
Parent → Child → Child → Child
Props only move one way (top → down)
Why props become difficult?
In small apps → OK
In big apps:
Too many components
You must pass props through every level
This becomes messy and hard to manage
(called prop drilling)
Solution: Context + useContext
Context lets you share data directly
useContext() helps you access that data easily
Instead of this
<App user="Priya">
<Header user="Priya">
<Profile user="Priya" />
</Header>
</App>
<UserContext value="Priya">
<App />
</UserContext>
const user = useContext(UserContext);
What happens?
No need to pass props everywhere
Any component can directly access data
Why use useContext?
Avoids prop drilling
summary
Props = pass data step-by-step
Context = share data globally
useContext() = access that shared data easily
Context
Create Context → Provide value → Use value in child
App → sends data
Child → receives data directly
No need to pass through intermediate components
summary
createContext() → create context
Provider → send data
useContext() → receive data
Avoids prop drilling
useContext
useContext() is a hook that lets you
get data from context directly
Data from context
Deep component example
App
└── Child
└── Child2
└── Child3
└── Child4
└── Child5
With Context
Just one Provider at top
Any child can access data directly
summary
useContext() = get shared data
Gets value from nearest Provider
Works for deeply nested components
Avoids prop drilling
Props vs. Context
Props
Props = passing data from parent → child step-by-step
Advantages
Good for small components
Disadvantages
If many components → messy
Need to pass data through every level (prop drilling)
Example
App → Header → Profile → Avatar
You must pass props through all components
Context
Context = share data directly to any component
Advantages
No need to pass props everywhere
Any component can access data
Good for large apps
Disadvantages
Can be harder to debug
Too many contexts → confusing
Example
<Context.Provider value="data">
<App />
</Context.Provider>
Any child can directly use it
Use Props when:
Data is needed by few components only
Components are not deeply nested
You want simple and clear flow
When to use Context?
Data is needed by many components
Components are deeply nested
Data is global (used everywhere)
summary
Props Context
Pass step-by-step Direct access
Good for small cases Good for large apps
Easy to debug Slightly complex
Small app → use Props
Large app → use Context
Wrap-up
When you have data that many components need,
you should use:
Context + useContext
No need to pass props again and again
Any component can access data directly
Use Context when:
App is large
Many components need same data
Data is global (like theme, user, language)
When NOT to use it?
Small/simple data
Data used in only 1–2 components
Props → for simple cases
Context → for shared/global data
useContext = easy way to access shared data in React apps
Top comments (0)