Skip to main content

Shared State & Context

In React, state is typically local to a component. However, there are cases where you want to share state across multiple components without passing props down through every level of the component tree. This is where React's Context API comes in handy.

1. Create the Context

// UserContext.tsx

import {
createContext,
useContext,
useState,
ReactNode,
} from 'react'

interface UserContextType {
name: string
setName: (name: string) => void
}

const UserContext = createContext<UserContextType | undefined>(undefined)

interface UserProviderProps {
children: ReactNode
}

export const UserProvider = ({
children,
}: UserProviderProps) => {
const [name, setName] = useState('René')

return (
<UserContext.Provider
value={{
name,
setName,
}}
>
{children}
</UserContext.Provider>
)
}

export const useUser = () => {
const context = useContext(UserContext)

if (!context) {
throw new Error('useUser must be used inside UserProvider')
}

return context
}

2. Wrap Your App

// App.tsx

import { UserProvider } from './UserContext'
import UserDisplay from './UserDisplay'
import UserInput from './UserInput'

const App = () => (
<UserProvider>
<UserDisplay />
<UserInput />
</UserProvider>
)

export default App

3. Read State

// UserDisplay.tsx

import { useUser } from './UserContext'

const UserDisplay = () => {
const { name } = useUser()

return <h2>Hello {name}</h2>
}

export default UserDisplay

4. Update State

// UserInput.tsx

import { useUser } from './UserContext'

const UserInput = () => {
const { name, setName } = useUser()

return (
<input
type="text"
value={name}
onChange={(event) =>
setName(event.target.value)
}
/>
)
}

export default UserInput

Result

When the user types in the input:

[ René ]

the value is updated in the Context and automatically reflected everywhere that uses:

const { name } = useUser()

This removes the need to pass props through multiple component levels ("prop drilling") and is often the first step toward application-wide state management in React.