See full working example here.
Create an interface/type to describe the shape of your context:
import { createContext } from 'react';
export interface CounterContextType {
counter: number;
increment: () => void;
decrement: () => void;
}
export const CounterContext = createContext<CounterContextType | null>(null);
Create a provider component:
import { useState } from 'react'
import { CounterContext, CounterContextType } from '../contexts/CounterContext'
export const CounterProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const [counter, setCounter] = useState(0)
const context: CounterContextType = {
counter,
increment: () => setCounter((prevValue) => prevValue + 1),
decrement: () => setCounter((prevValue) => prevValue - 1),
}
return (
<CounterContext.Provider value={context}>
{children}
</CounterContext.Provider>
)
}
Create a hook that lets you use the context:
import { useContext } from 'react';
import { CounterContext } from '../contexts/CounterContext';
export function useCounter() {
const counter = useContext(CounterContext);
if (!counter) {
throw new Error(
'useCounter has to be used within <CounterContext.Provider>'
);
}
return counter;
}
Consume the context via your hook:
import './App.css'
import { useCounter } from './hooks/useCounter'
function App() {
const { counter, increment, decrement } = useCounter()
return (
<>
<div>Clicks: {counter}</div>
<div style={{ display: 'flex', gap: '10px', marginTop: '15px' }}>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
</>
)
}
export default App