Learn how to share data across components and manage global state more effectively.
Without context, data must thread through every component in between - even ones that don't use it.
Live demo
Section must accept and forward color even though it never uses it. Add more levels and this becomes painful fast.
Code
function App() {
const [color, setColor] =
useState('#EBDEFB')
return <Section color={color} />
}
function Section({ color }) {
return <Button color={color} />
}
function Button({ color }) {
return (
<div style={{ background: color }}>
My favourite color!
</div>
)
}Context lets you beam data directly to any descendant - no intermediate props required. There are two steps: providing and consuming.
Create the context
Call createContext() once — at the top of a file or in its own module. The argument is the fallback value used when no Provider exists above in the tree.
import { createContext } from 'react'
// The argument is the default value,
// used when there is no Provider above.
export const FavouriteColorContext = createContext<string>('#EBDEFB')Provide - broadcast the value
Wrap the subtree that needs the data in a Provider. Whatever you pass as value is broadcast to all descendants. Change the colour below to see it update live.
<FavouriteColorContext.Provider value="#EBDEFB">
<DeepChild>
no props passed ✓
</FavouriteColorContext.Provider>
function App() {
const [color, setColor] =
useState('#EBDEFB')
return (
<FavouriteColorContext.Provider
value={color}
>
<Home /> {/* any depth */}
</FavouriteColorContext.Provider>
)
}Consume - read with useContext
Any descendant can read the context value without receiving any props. useContext subscribes the component it re-renders automatically when the value changes.
import { useContext } from 'react'
import { FavouriteColorContext } from './App'
function DeepChild() {
const color = useContext(FavouriteColorContext)
return (
<div style={{ background: color }}>
My favourite color!
</div>
)
}Update - pass the setter too
Pass both the value and its setter as the context value. Any descendant can then read and update the shared state. The deep child below does both - try the swatches.
<ColorContext.Provider value={{ color, setColor }}>
<Layout> — no color prop needed
<DeepChild> — reads AND writes
</ColorContext.Provider>
type ColorCtx = {
color: string
setColor: (c: string) => void
}
const ColorContext = createContext<ColorCtx>(...)
// Provide both together
<ColorContext.Provider
value={{ color, setColor }}
>
<App />
</ColorContext.Provider>
// Deep child reads AND writes
function DeepChild() {
const { color, setColor } =
useContext(ColorContext)
}