Claude Code Qwik Store Reactive State Management Guide
Qwik’s unique approach to reactivity sets it apart from traditional JavaScript frameworks. Instead of hydrating entire applications on the client, Qwik uses resumability—serializing state into the HTML and resuming execution where the server left off. Understanding how to manage this reactive state is essential for building performant Qwik applications.
This guide covers Qwik’s core state management primitives: useStore for reactive objects and useSignal for primitive values, along with patterns for building scalable state management in your Qwik projects.
Understanding Qwik’s Reactivity Model
Unlike React’s virtual DOM diffing or Vue’s proxy-based reactivity, Qwik uses a fine-grained reactive system that tracks dependencies at the component level. When you modify reactive state, Qwik only updates the specific DOM nodes that depend on that changed value.
The key primitives you’ll work with are:
- useSignal: For primitive values (strings, numbers, booleans)
- useStore: For complex objects and nested reactivity
- useComputed: For derived values that automatically update
import { component$, useStore, useSignal, useComputed$ } from '@builder.io/qwik';
export default component$(() => {
// Primitive state - use useSignal
const count = useSignal(0);
// Complex state - use useStore
const user = useStore({
name: 'John',
preferences: {
theme: 'dark',
notifications: true
}
});
// Derived state
const doubled = useComputed$(() => count.value * 2);
return (
<div>
<p>Count: {count.value}</p>
<p>Doubled: {doubled.value}</p>
<p>User: {user.name}</p>
<button onClick$={() => count.value++}>Increment</button>
</div>
);
});
useStore Deep Dive
The useStore hook creates a reactive object that Qwik tracks at a fine-grained level. The second argument lets you configure reactivity options.
Basic Store Usage
interface Todo {
id: number;
text: string;
completed: boolean;
}
export const TodoList = component$(() => {
const store = useStore({
todos: [] as Todo[],
filter: 'all' as 'all' | 'active' | 'completed'
});
return (
<div>
<select
value={store.filter}
onChange$={(e) => store.filter = (e.target as HTMLSelectElement).value as any}
>
<option value="all">All</option>
<option value="active">Active</option>
<option value="completed">Completed</option>
</select>
<ul>
{store.todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange$={(e) => todo.completed = (e.target as HTMLInputElement).checked}
/>
{todo.text}
</li>
))}
</ul>
</div>
);
});
Nested Reactivity with useStore
One of Qwik’s powerful features is deep reactivity. By default, changes to nested properties trigger updates:
export const NestedStore = component$(() => {
const store = useStore({
config: {
api: {
endpoint: '/api/v1',
timeout: 5000
}
}
});
return (
<div>
<button onClick$={() => {
// This deeply nested change still triggers reactivity
store.config.api.timeout = 10000;
}}>
Update Timeout
</button>
</div>
);
});
However, you can optimize performance by using the reactive option set to false for flat structures:
const flatStore = useStore({
prop1: 'value1',
prop2: 'value2'
}, { reactive: false });
useSignal for Primitive Values
Use useSignal when you have single primitive values. Signals are more performant than stores for simple values because they have less overhead.
export const SignalCounter = component$(() => {
const count = useSignal(0);
const name = useSignal('Guest');
const isLoading = useSignal(false);
return (
<div>
<h1>Hello, {name.value}!</h1>
<p>Count: {count.value}</p>
<button
onClick$={() => count.value++}
disabled={isLoading.value}
>
Increment
</button>
<button onClick$={() => isLoading.value = !isLoading.value}>
Toggle Loading
</button>
</div>
);
});
Sharing State Across Components
Qwik provides several patterns for sharing state between components.
Props Drilling with Signals
Pass signals down as props for parent-child communication:
interface Props {
count: { value: number };
}
export const Parent = component$(() => {
const sharedCount = useSignal(0);
return (
<div>
<Child count={sharedCount} />
<button onClick$={() => sharedCount.value++}>
Parent Increment
</button>
</div>
);
});
export const Child = component$<Props>(({ count }) => {
return (
<div>
<p>Child sees: {count.value}</p>
<button onClick$={() => count.value++}>
Child Increment
</button>
</div>
);
});
Using Context for Global State
For truly global state, use Qwik’s context API:
import { createContextId, useContext, useContextProvider, useStore } from '@builder.io/qwik';
interface AppState {
theme: 'light' | 'dark';
user: { name: string } | null;
}
export const AppContext = createContextId<AppState>('app-context');
export const AppProvider = component$(() => {
const appState = useStore<AppState>({
theme: 'light',
user: null
});
useContextProvider(AppContext, appState);
return <Slot />;
});
// Using the context in any component
export const ThemedComponent = component$(() => {
const appState = useContext(AppContext);
return (
<button onClick$={() => {
appState.theme = appState.theme === 'light' ? 'dark' : 'light';
}}>
Current: {appState.theme}
</button>
);
});
Best Practices for Qwik State Management
Follow these practices to build maintainable Qwik applications:
-
Choose the right primitive: Use
useSignalfor primitives,useStorefor objects. This optimization matters in large applications. -
Minimize reactivity scope: When possible, use
{ reactive: false }on stores that don’t need deep tracking. -
Keep state co-located: Define state in the component that needs it. Only lift state up when truly necessary.
-
Use computed values wisely:
useComputed$caches results and only recalculates when dependencies change, making it efficient for derived state. -
Leverage $ suffix: Remember that event handlers ending with
$are lazy-loaded. This is fundamental to Qwik’s performance model.
// Good: Derived value with useComputed$
const fullName = useComputed$(() =>
`${user.value.firstName} ${user.value.lastName}`
);
// Avoid: Computing derived values during render
// const fullName = user.value.firstName + ' ' + user.value.lastName;
Conclusion
Qwik’s reactive state management offers a fresh perspective on building web applications. By understanding when to use useSignal versus useStore, using context for global state, and following best practices for reactivity, you can build applications that are both highly performant and easy to maintain.
The key insight is that Qwik’s fine-grained reactivity means you don’t need to think about memoization or optimization strategies that plague other frameworks—Qwik handles this automatically at the framework level.
Related Reading
- Claude Code for Beginners: Complete Getting Started Guide
- Best Claude Skills for Developers in 2026
- Claude Skills Guides Hub
Built by theluckystrike — More at zovo.one