Getting Started
Let's get you up and running with zustand-context
in just a few steps.
Installation
- npm
- yarn
- pnpm
npm install @mag1yar/zustand-context
yarn add @mag1yar/zustand-context
pnpm add @mag1yar/zustand-context
Quick Example
Here's a minimal example showing how to use zustand-context:
- TypeScript
- JavaScript
import { create } from '@mag1yar/zustand-context';
// Define your state type
interface CounterState {
count: number;
increment: () => void;
decrement: () => void;
}
// 1. Create a context-aware store
const useCounterStore = create<CounterState>(
(set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}),
{
name: 'Counter', // Required: name for your context
},
);
// 2. Create components that use the store
function Counter() {
const count = useCounterStore((state) => state.count);
const increment = useCounterStore((state) => state.increment);
return (
<div>
<h2>Count: {count}</h2>
<button onClick={increment}>Increment</button>
</div>
);
}
// 3. Use the Provider to create isolated instances
function App() {
return (
<div>
<h1>Multiple Counters</h1>
{/* First counter instance */}
<div className="counter-container">
<h3>Counter 1</h3>
<useCounterStore.Provider initialState={{ count: 5 }}>
<Counter />
</useCounterStore.Provider>
</div>
{/* Second counter instance */}
<div className="counter-container">
<h3>Counter 2</h3>
<useCounterStore.Provider initialState={{ count: 10 }}>
<Counter />
</useCounterStore.Provider>
</div>
</div>
);
}
import { create } from '@mag1yar/zustand-context';
// 1. Create a context-aware store
const useCounterStore = create(
(set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}),
{
name: 'Counter', // Required: name for your context
},
);
// 2. Create components that use the store
function Counter() {
const count = useCounterStore((state) => state.count);
const increment = useCounterStore((state) => state.increment);
return (
<div>
<h2>Count: {count}</h2>
<button onClick={increment}>Increment</button>
</div>
);
}
// 3. Use the Provider to create isolated instances
function App() {
return (
<div>
<h1>Multiple Counters</h1>
{/* First counter instance */}
<div className="counter-container">
<h3>Counter 1</h3>
<useCounterStore.Provider initialState={{ count: 5 }}>
<Counter />
</useCounterStore.Provider>
</div>
{/* Second counter instance */}
<div className="counter-container">
<h3>Counter 2</h3>
<useCounterStore.Provider initialState={{ count: 10 }}>
<Counter />
</useCounterStore.Provider>
</div>
</div>
);
}
Understanding Component Access
Components can only access store instances that are available in their provider hierarchy:
- TypeScript
- JavaScript
function App() {
return (
<useCounterStore.Provider instanceId="root">
{/* This component can access the "root" instance */}
<Counter />
<useCounterStore.Provider instanceId="nested">
{/* This component can access both "root" and "nested" instances */}
<NestedComponent />
</useCounterStore.Provider>
{/* This component can ONLY access the "root" instance */}
<AnotherComponent />
</useCounterStore.Provider>
);
}
function NestedComponent() {
// Access the nearest provider (which is "nested")
const nestedCount = useCounterStore((state) => state.count);
// Access a specific parent provider by ID
const rootCount = useCounterStore((state) => state.count, { from: 'root' });
return (
<div>
<p>Nested count: {nestedCount}</p>
<p>Root count: {rootCount}</p>
</div>
);
}
function App() {
return (
<useCounterStore.Provider instanceId="root">
{/* This component can access the "root" instance */}
<Counter />
<useCounterStore.Provider instanceId="nested">
{/* This component can access both "root" and "nested" instances */}
<NestedComponent />
</useCounterStore.Provider>
{/* This component can ONLY access the "root" instance */}
<AnotherComponent />
</useCounterStore.Provider>
);
}
function NestedComponent() {
// Access the nearest provider (which is "nested")
const nestedCount = useCounterStore((state) => state.count);
// Access a specific parent provider by ID
const rootCount = useCounterStore((state) => state.count, { from: 'root' });
return (
<div>
<p>Nested count: {nestedCount}</p>
<p>Root count: {rootCount}</p>
</div>
);
}
Accessing Multiple Instances
To access multiple store instances from a single component, the component must be within the hierarchy of all providers it needs to access:
Proper Provider Hierarchy
- TypeScript
- JavaScript
function App() {
return (
// Common parent provider that will contain both instances
<useCounterStore.Provider instanceId="root">
{/* First instance */}
<useCounterStore.Provider instanceId="counter1" initialState={{ count: 5 }}>
<div className="counter-section">
<h3>Counter 1</h3>
<Counter />
{/* Second instance nested inside first */}
<useCounterStore.Provider instanceId="counter2" initialState={{ count: 10 }}>
<div className="counter-section">
<h3>Counter 2</h3>
<Counter />
{/* This component can access both counters because it's inside BOTH providers */}
<CounterSummary />
</div>
</useCounterStore.Provider>
</div>
</useCounterStore.Provider>
</useCounterStore.Provider>
);
}
function CounterSummary() {
const counter1 = useCounterStore((s) => s.count, { from: 'counter1' });
const counter2 = useCounterStore((s) => s.count, { from: 'counter2' });
return <p>Total: {counter1 + counter2}</p>;
}
function App() {
return (
// Common parent provider that will contain both instances
<useCounterStore.Provider instanceId="root">
{/* First instance */}
<useCounterStore.Provider instanceId="counter1" initialState={{ count: 5 }}>
<div className="counter-section">
<h3>Counter 1</h3>
<Counter />
{/* Second instance nested inside first */}
<useCounterStore.Provider instanceId="counter2" initialState={{ count: 10 }}>
<div className="counter-section">
<h3>Counter 2</h3>
<Counter />
{/* This component can access both counters because it's inside BOTH providers */}
<CounterSummary />
</div>
</useCounterStore.Provider>
</div>
</useCounterStore.Provider>
</useCounterStore.Provider>
);
}
function CounterSummary() {
const counter1 = useCounterStore((s) => s.count, { from: 'counter1' });
const counter2 = useCounterStore((s) => s.count, { from: 'counter2' });
return <p>Total: {counter1 + counter2}</p>;
}
Alternative: Provider-Specific Components
Another approach is to create components that each access only one provider:
- TypeScript
- JavaScript
function App() {
return (
<div>
<useCounterStore.Provider instanceId="counter1" initialState={{ count: 5 }}>
<div className="counter-section">
<h3>Counter 1</h3>
<Counter />
<Counter1Observer />
</div>
</useCounterStore.Provider>
<useCounterStore.Provider instanceId="counter2" initialState={{ count: 10 }}>
<div className="counter-section">
<h3>Counter 2</h3>
<Counter />
<Counter2Observer />
</div>
</useCounterStore.Provider>
{/* Display summaries from both observers */}
<DisplaySummary />
</div>
);
}
// These components access their respective providers and could use
// other state management techniques to share data between them
function Counter1Observer() {
const count = useCounterStore((s) => s.count);
// Update some external state or context with this value
return null;
}
function Counter2Observer() {
const count = useCounterStore((s) => s.count);
// Update some external state or context with this value
return null;
}
function App() {
return (
<div>
<useCounterStore.Provider instanceId="counter1" initialState={{ count: 5 }}>
<div className="counter-section">
<h3>Counter 1</h3>
<Counter />
<Counter1Observer />
</div>
</useCounterStore.Provider>
<useCounterStore.Provider instanceId="counter2" initialState={{ count: 10 }}>
<div className="counter-section">
<h3>Counter 2</h3>
<Counter />
<Counter2Observer />
</div>
</useCounterStore.Provider>
{/* Display summaries from both observers */}
<DisplaySummary />
</div>
);
}
// These components access their respective providers and could use
// other state management techniques to share data between them
function Counter1Observer() {
const count = useCounterStore((s) => s.count);
// Update some external state or context with this value
return null;
}
function Counter2Observer() {
const count = useCounterStore((s) => s.count);
// Update some external state or context with this value
return null;
}