Context API in React
Each new version of React gives us better and more interesting tools that are meant to improve the quality of our work with the code we write. Context API, introduced in React 16.3, is no different.

From this article you will learn:
- What Context API is
- Why it was created and which problems it is meant to solve
- How to use Context API with props and with the useContext hook
How Can Context API Help Us?
In a classic application written in React, data is passed from top to bottom between components using props. Sometimes a component nested a few levels lower needs data from the top-level component. This leads to a situation where we have to pass props down through several components.
import React, { useState } from "react";
const App = () => {
const defaultUser = {
name: "",
avatarUrl: ""
};
const [loggedUser, logInUser] = useState(defaultUser);
return (
<>
<button onClick={logInUser} />
<Header user={loggedUser} />
</>
);
};
const Header = ({ user }) => <UserPanel user={user} />;
const UserPanel = ({ user }) => (
<>
<UserAvatar user={user} />
<UserName user={user} />
</>
);
const UserName = ({ user }) => <h2>{user.name}</h2>;
const UserAvatar = ({ user }) => <img src={user.avatarUrl} alt={user.name} />;In the example above, we can see that information about the logged-in user is needed in the <UserAvatar /> and <UserName /> components, while the login itself happens in the top-level <App /> component. To make the logged-in user data available to the components placed lowest in the Virtual DOM tree, we have to pass it with props. This technique is called props drilling.
In theory, we could keep information about our user in a global object, but as a rule, storing data in global objects is not the best solution. We would have much less control over what happens to our object, and where and how it is modified.
As an alternative to passing data with props drilling, we can use tools such as Redux or MobX. However, we have to answer the question of whether it pays off to attach another library to our project. Remember that we will increase the amount of code, add quite a lot of extra abstraction, and increase the maintenance cost of our application.
React Context API is another option for solving the problem described above.
Context API in Practice
Context API definitely makes work easier in situations where lower-level components need to know about changes or need access to specific data. Building a context means calling a single function exposed by the React API.
Context, Provider, Consumer
import React from "react";
const defaultUser = {
name: "",
avatarUrl: "//placehold.it/45x45"
};
const userContext = React.createContext(defaultUser);
const { Provider, Consumer } = userContext;
export default userContext;Creating a context is, in practice, calling one function that accepts a default value. The default value will be useful in two cases:
- when Consumer does not find a Provider
- when Provider does not have the value prop assigned
Above, I mentioned two new components: Consumer and Provider. The createContext function returns two new components. One of them is responsible for providing the context to its children (<Provider />), while the other is used to read the current value of the context (<Consumer />).
Provider requires one prop: value. value is the default value that will be passed to Consumer first. As you can see in the code below, in the state of the App component we keep the user variable, which is updated after clicking the button. Every state change in the App component will emit information about that change to Consumers.
Notice that neither the Hi component nor the Header component accepts user in props.
import React from "react";
import userContext from "./userContext";
const { Consumer } = userContext;
const Hi = () => (
<Consumer>
{
user => <h2>{`Hi, ${user.name}`}</h2>
}
</Consumer>
);
export default Hi;import React from "react";
const Header = () => (
<div>
<nav>...</nav>
<Hi />
</div>
);
export default Header;import React, { useState } from "react";
import Header from "./header";
import userContext from "./userContext";
const App = () => {
const { Provider } = userContext;
const [user, logIn] = useState({
name: "",
avatarUrl: "//placehold.it/45x45"
});
return (
<Provider value={user}>
<Header />
<button onClick={() => logIn({ name: "Mateusz", avatar: "" })}>
Log in
</button>
</Provider>
);
};Render Props
In the example above, we have one of several ways to extract data in the Consumer component. The child of our Consumer is a function. Our function accepts one parameter, which is exactly the same value that was passed to the value prop of our Provider. An implementation where a function is the child of a component is called render props. This technique is used to share code between different components. You will find more about this in the documentation.
<Consumer>
user => <h2>{`Hi, ${user.name}`}</h2>
</Consumer>useContext
Another way to extract data from Consumer is to use the useContext hook, which comes together with React. Using useContext reduces the complexity of our code. Example:
import React, { useContext } from "react";
import userContext from "./userContext";
const Hi = () => {
const user = useContext(userContext);
return <h2>{`Hi, ${user.name}`}</h2>;
};
export default Hi;useContext accepts our context as an argument and returns the subscribed value returned by Provider.
Practical Use
Is it worth using Context API always and everywhere? The answer to this question is very difficult, because it depends on many factors. Sometimes props drilling is necessary. Sometimes using libraries such as Redux or MobX may be more justified than using the new thing that Context API was at the time.
Three of the most popular examples are very often given as cases where Context API is worth using.
Themes
Improving User Experience is always a good idea. With Context API, implementing theme management in our project is much easier. Switching between dark mode and light mode with Context API can be very simple. Our context can keep the right settings in an object. With Context API, switching the theme will not require passing more props, which would undoubtedly reduce the readability of our code.
Multilingual Support
Just like in the case of changing a theme, changing the language version with Context API will also be a very good idea.
Authorization
It was not without reason that I chose authorization as the simple example in my post. It is probably the most obvious example of how Context API can help us. Passing user data through the whole component tree does not sound like a good idea, especially since most components will not actually need that data.
Summary
Context API is another very good tool added to React. It seems to me that it can be very useful and can help solve many cases that, until now, could keep us awake at night. It is worth noting that Context API cannot replace Redux or MobX, because each of these tools can be useful in a different way. Let us also remember that we do not always have to harness an additional library to manage the state of our application.




Comments (0)
No one has posted anything yet, but that means.... you may be the first.