React Context has been a lifesaver in my development career, especially when dealing with deep component nesting.
In this tutorial, I will show you exactly how to implement React Context in functional components using the modern useContext hook.
What is React Context?
In my eight years of building React applications, the biggest headache was always “prop drilling.”
Prop drilling happens when you pass data through many layers of components that don’t even need that data.
React Context provides a way to share values like themes or user data between components without having to explicitly pass a prop through every level of the tree.
Context in Functional Components
Functional components are the standard in modern React development, and they make state management much cleaner.
Using the useContext hook allows you to subscribe to context changes with just one line of code.
I find this much more readable than the old “Consumer” wrapper components we used to use in class-based projects.
Method 1: The Basic useContext Hook Approach
This is the most common way I implement Context in my day-to-day work. It involves three simple steps: creating, providing, and consuming.
For this example, let’s imagine we are building a US Tax Bracket Calculator where the tax year needs to be accessible everywhere.
Step 1: Create the Context
First, we create a context object using React.createContext().
import React, { createContext } from 'react';
// We initialize the context with a default value
export const TaxYearContext = createContext('2024');Step 2: Provide the Context
The Provider component wraps the part of your app that needs access to the data.
import React, { useState } from 'react';
import { TaxYearContext } from './TaxYearContext';
import TaxDashboard from './TaxDashboard';
const App = () => {
const [taxYear, setTaxYear] = useState('2024');
return (
<div style={{ padding: '20px' }}>
<h1>IRS Tax Assistant - USA</h1>
<TaxYearContext.Provider value={taxYear}>
<TaxDashboard />
</TaxYearContext.Provider>
</div>
);
};
export default App;Step 3: Consume the Context
Now, any functional component inside the Provider can grab the value using the useContext hook.
import React, { useContext } from 'react';
import { TaxYearContext } from './TaxYearContext';
const TaxDashboard = () => {
// Accessing the context value directly
const year = useContext(TaxYearContext);
return (
<div style={{ border: '1px solid #ccc', padding: '10px' }}>
<h3>Current Filing Status</h3>
<p>You are currently viewing tax regulations for the year: <strong>{year}</strong></p>
<p>Location: United States (Federal)</p>
</div>
);
};
export default TaxDashboard;You can see the output in the screenshot below.

Method 2: Manage Complex State with Context and useReducer
Sometimes, just passing a string isn’t enough. In professional-grade apps, I often combine Context with useReducer.
Let’s look at a High-Yield Savings Account (HYSA) dashboard example where we manage a balance and transaction history.
Here is how I set up a robust state management system for a banking feature.
import React, { createContext, useReducer, useContext } from 'react';
// 1. Define Initial State
const initialState = {
balance: 5000.00,
accountType: 'Premium Savings',
bankName: 'Chase Bank - NYC Branch'
};
// 2. Define Reducer
const bankReducer = (state, action) => {
switch (action.type) {
case 'DEPOSIT':
return { ...state, balance: state.balance + action.payload };
case 'WITHDRAW':
return { ...state, balance: state.balance - action.payload };
default:
return state;
}
};
// 3. Create Context
const BankContext = createContext();
// 4. Create a Custom Provider Component
export const BankProvider = ({ children }) => {
const [state, dispatch] = useReducer(bankReducer, initialState);
return (
<BankContext.Provider value={{ state, dispatch }}>
{children}
</BankContext.Provider>
);
};
// 5. Build the UI Components
const AccountSummary = () => {
const { state } = useContext(BankContext);
return (
<div className="account-card">
<h2>{state.bankName}</h2>
<p>Account Type: {state.accountType}</p>
<h3>Current Balance: ${state.balance.toLocaleString('en-US')}</h3>
</div>
);
};
const TransactionButtons = () => {
const { dispatch } = useContext(BankContext);
return (
<div>
<button onClick={() => dispatch({ type: 'DEPOSIT', payload: 500 })}>
Deposit $500 (Stimulus Check)
</button>
<button onClick={() => dispatch({ type: 'WITHDRAW', payload: 200 })}>
Withdraw $200 (ATM)
</button>
</div>
);
};
// 6. Main App Component
const BankingApp = () => {
return (
<BankProvider>
<div style={{ fontFamily: 'Arial, sans-serif', maxWidth: '500px' }}>
<AccountSummary />
<hr />
<TransactionButtons />
</div>
</BankProvider>
);
};
export default BankingApp;You can see the output in the screenshot below.

When Should You Avoid Context?
In my experience, Context is not a “silver bullet” for every state problem.
If you have data that changes every millisecond (like a mouse position or a countdown timer), Context can cause performance issues.
This happens because every component that consumes the context will re-render whenever the value changes.
For heavy lifting or global states that change frequently, I still lean toward libraries like Redux or Zustand.
Create a Custom Hook for Better Code Cleanliness
One trick I use to make my code look professional is hiding the useContext logic inside a custom hook.
This prevents you from having to import both the Context object and the useContext hook in every single file.
// Inside your Context file
export const useBank = () => {
const context = useContext(BankContext);
if (!context) {
throw new Error('useBank must be used within a BankProvider');
}
return context;
};
// Inside your Component file
// Instead of: const { state } = useContext(BankContext);
// You simply use:
const { state } = useBank();Performance Optimization with React.memo
When using Context, you might notice components re-rendering unnecessarily. To fix this, I often wrap my sub-components in React.memo.
This ensures that the component only re-renders if its specific props change, even if the Context provider above it updates.
Summary of Key Points
- CreateContext: Defines the data store.
- Provider: Wraps the components and passes the “value.”
- useContext: The hook used inside functional components to read the data.
- Custom Hooks: A cleaner way to export your context logic.
Implementing React Context correctly will make your codebase much easier to maintain.
It allows your team to understand the data flow without tracing props through ten different files.
I hope you found this tutorial helpful! Using these methods has significantly improved the architecture of the enterprise React apps I’ve built over the years.
In this tutorial, I’ve covered how to use React Context in functional components using different methods and real-world USA-themed examples.
You may also like to read:
- React Controlled Component
- How to Use Angular Components in React
- How to Create a Horizontal Timeline in React
- How to Build a React File Explorer Component

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.