Provider Component
The .Provider
component is attached to every store created with zustand-context
. It creates a boundary for your store instance and makes it available to all child components.
Basic Usage
- TypeScript
- JavaScript
<useCounterStore.Provider>
<Counter />
</useCounterStore.Provider>
<useCounterStore.Provider>
<Counter />
</useCounterStore.Provider>
API Reference
Props
- TypeScript
- JavaScript
interface ProviderProps<T> {
/** Optional ID for this instance */
instanceId?: string;
/** Optional initial state (partial) */
initialState?: DeepPartial<T>;
/** React children */
children: React.ReactNode;
}
{
// Optional ID for this instance
instanceId?: string,
// Optional initial state (partial)
initialState?: object,
// React children
children: React.ReactNode
}
instanceId (optional)
A unique identifier for this instance of the store. Used to:
- Reference this specific instance using the
from
option - Distinguish between multiple instances of the same store
- Provide meaningful names in debugging and error messages
If not provided or set to an empty string, a default internal identifier will be used.
- TypeScript
- JavaScript
<useCounterStore.Provider instanceId="header">
<HeaderCounter />
</useCounterStore.Provider>
<useCounterStore.Provider instanceId="sidebar">
<SidebarCounter />
</useCounterStore.Provider>
<useCounterStore.Provider instanceId="header">
<HeaderCounter />
</useCounterStore.Provider>
<useCounterStore.Provider instanceId="sidebar">
<SidebarCounter />
</useCounterStore.Provider>
initialState (optional)
A partial state object that overrides the default state values for this instance. Only the properties you include will be overridden.
- TypeScript
- JavaScript
<useCounterStore.Provider initialState={{ count: 5 }}>
<Counter />
</useCounterStore.Provider>
<useCounterStore.Provider initialState={{ count: 5 }}>
<Counter />
</useCounterStore.Provider>
For nested state, you only need to include the properties you want to override:
- TypeScript
- JavaScript
<useUserStore.Provider
initialState={{
profile: {
// Only override firstName, other profile properties remain unchanged
firstName: 'Jane',
},
}}>
<UserProfile />
</useUserStore.Provider>
<useUserStore.Provider
initialState={{
profile: {
// Only override firstName, other profile properties remain unchanged
firstName: 'Jane',
},
}}>
<UserProfile />
</useUserStore.Provider>
Provider Behaviors
Instance Creation
When a Provider is mounted, it:
- Creates a new store instance
- Initializes it with the store's default state (from the initializer function in
create
) - Applies any
initialState
values if provided - Makes the instance accessible to all child components
Provider Isolation
Each Provider creates an isolated instance:
- TypeScript
- JavaScript
<useCounterStore.Provider initialState={{ count: 5 }}>
{/* This counter starts at 5 */}
<Counter />
</useCounterStore.Provider>
<useCounterStore.Provider initialState={{ count: 10 }}>
{/* This counter starts at 10 and is completely independent */}
<Counter />
</useCounterStore.Provider>
<useCounterStore.Provider initialState={{ count: 5 }}>
{/* This counter starts at 5 */}
<Counter />
</useCounterStore.Provider>
<useCounterStore.Provider initialState={{ count: 10 }}>
{/* This counter starts at 10 and is completely independent */}
<Counter />
</useCounterStore.Provider>
Multiple Providers
You can have multiple providers with different IDs to create separate instances:
- TypeScript
- JavaScript
function App() {
return (
<div>
{/* First counter instance */}
<useCounterStore.Provider instanceId="counter1" initialState={{ count: 5 }}>
<div>
<h3>Counter 1</h3>
<Counter />
</div>
</useCounterStore.Provider>
{/* Second counter instance */}
<useCounterStore.Provider instanceId="counter2" initialState={{ count: 10 }}>
<div>
<h3>Counter 2</h3>
<Counter />
</div>
</useCounterStore.Provider>
</div>
);
}
function App() {
return (
<div>
{/* First counter instance */}
<useCounterStore.Provider instanceId="counter1" initialState={{ count: 5 }}>
<div>
<h3>Counter 1</h3>
<Counter />
</div>
</useCounterStore.Provider>
{/* Second counter instance */}
<useCounterStore.Provider instanceId="counter2" initialState={{ count: 10 }}>
<div>
<h3>Counter 2</h3>
<Counter />
</div>
</useCounterStore.Provider>
</div>
);
}
Common Use Patterns
Feature-Specific Providers
Wrap different features with their own providers:
- TypeScript
- JavaScript
function App() {
return (
<div>
<useAuthStore.Provider>
<Header />
<Sidebar />
</useAuthStore.Provider>
<useCartStore.Provider>
<ShoppingCart />
</useCartStore.Provider>
</div>
);
}
function App() {
return (
<div>
<useAuthStore.Provider>
<Header />
<Sidebar />
</useAuthStore.Provider>
<useCartStore.Provider>
<ShoppingCart />
</useCartStore.Provider>
</div>
);
}
Reusable Components with Local State
Create reusable components with their own isolated state:
- TypeScript
- JavaScript
function DataTable({ data, columns }) {
return (
<useTableStore.Provider
initialState={{
rows: data,
columns,
sortBy: columns[0].id,
sortDirection: 'asc',
}}>
<TableHeader />
<TableBody />
<TableFooter />
</useTableStore.Provider>
);
}
// Usage
function ReportsPage() {
return (
<div>
<DataTable data={usersData} columns={userColumns} />
<DataTable data={ordersData} columns={orderColumns} />
</div>
);
}
function DataTable({ data, columns }) {
return (
<useTableStore.Provider
initialState={{
rows: data,
columns,
sortBy: columns[0].id,
sortDirection: 'asc',
}}>
<TableHeader />
<TableBody />
<TableFooter />
</useTableStore.Provider>
);
}
// Usage
function ReportsPage() {
return (
<div>
<DataTable data={usersData} columns={userColumns} />
<DataTable data={ordersData} columns={orderColumns} />
</div>
);
}
Accessing Multiple Providers
To access multiple providers, you must structure your components so they're within the Provider hierarchy:
- TypeScript
- JavaScript
function App() {
return (
<div>
{/* Root provider with common parent */}
<useCounterStore.Provider instanceId="root">
{/* First counter provider */}
<useCounterStore.Provider instanceId="counter1" initialState={{ count: 5 }}>
<Counter label="Counter 1" />
{/* Second counter provider nested inside first */}
<useCounterStore.Provider instanceId="counter2" initialState={{ count: 10 }}>
<Counter label="Counter 2" />
{/* This component can access both counters */}
<CountersTotal />
</useCounterStore.Provider>
</useCounterStore.Provider>
</useCounterStore.Provider>
</div>
);
}
function CountersTotal() {
// Access specific instances by ID
const counter1Value = useCounterStore((s) => s.count, { from: 'counter1' });
const counter2Value = useCounterStore((s) => s.count, { from: 'counter2' });
return <p>Total: {counter1Value + counter2Value}</p>;
}
function App() {
return (
<div>
{/* Root provider with common parent */}
<useCounterStore.Provider instanceId="root">
{/* First counter provider */}
<useCounterStore.Provider instanceId="counter1" initialState={{ count: 5 }}>
<Counter label="Counter 1" />
{/* Second counter provider nested inside first */}
<useCounterStore.Provider instanceId="counter2" initialState={{ count: 10 }}>
<Counter label="Counter 2" />
{/* This component can access both counters */}
<CountersTotal />
</useCounterStore.Provider>
</useCounterStore.Provider>
</useCounterStore.Provider>
</div>
);
}
function CountersTotal() {
// Access specific instances by ID
const counter1Value = useCounterStore((s) => s.count, { from: 'counter1' });
const counter2Value = useCounterStore((s) => s.count, { from: 'counter2' });
return <p>Total: {counter1Value + counter2Value}</p>;
}