Large state management can become a source of excessive re-renders. This episode covers how to control re-renders with storeToRefs and granular state, using $subscribe with the detached option, and batching many updates with $patch for better performance.

The bigger the app, the easier it is for a store to become a source of unnecessary re-renders. Every state field that changes can trigger updates in the components that read it — and if every component reads the whole store, one small change ends up in a mass re-render.
Episode 13 covers Pinia reactivity tuning: controlling re-renders with storeToRefs and granular access, using $subscribe with the detached option, and batching updates with $patch. This isn't micro-optimization — these are patterns that keep the app from slowing down as stores grow.
Notice how the way you read affects re-renders:
// Re-renders when the used fields change
const { count, double } = storeToRefs(store)
// Wider potential re-renders: the template reads many properties
const store = useCounterStore()const { count, double } = storeToRefs(store) takes only the fields you need, so Vue only tracks those dependencies. Conversely, using the full store and reading many properties can create more dependencies — not wrong, but less precise.
Don't put all your app's data in one big store. Split it into small stores and specific state:
// Bad: one state holds many domains
state: () => ({ user: {}, cart: [], ui: { modal: null } })
// Better: separate per store
useAuthStore() // user
useCartStore() // cart
useUiStore() // modal, sidebarWith separate stores, a component that only needs the cart won't be affected when ui changes. This principle reduces re-renders and makes dependency tracking sharper.
By default, $subscribe stops when the component that registered it is unmounted. For subscribers that need to live longer, pass { detached: true }:
store.$subscribe(
(_mutation, state) => {
saveToStorage(state)
},
{ detached: true },
){ detached: true } keeps the subscriber active even after the component is unmounted. Use it for long-running tasks like autosave. Otherwise, leave the default so subscribers are cleaned up along with the component and don't leak.
Several sequential assignments trigger many updates. $patch merges them into one:
// Three separate updates
store.firstName = 'Arman'
store.lastName = 'Dwi'
store.age = 26
// One patch
store.$patch({
firstName: 'Arman',
lastName: 'Dwi',
age: 26,
})store.$patch({ ... }) groups changes so subscribers and DevTools only record a single mutation. This matters for more efficient storage sync and observability.
Pinia provides two ways to monitor changes: the store's $subscribe and Vue's watch. When to use which?
$subscribe only detects state changes after they've been successfully mutated, and { flush: 'sync' } can be set so the callback runs before the component re-renders. Suited for external sync like storage or analytics.watch(store.$state, ...) uses Vue's regular watch mechanism, supports deep and immediate, and is easier to stop if the source is your own composable.watch(
() => store.$state.cart,
(cart) => {
console.log('cart changed', cart)
},
{ deep: true },
)For simple cases, choose the one that uses the least external API: watch when you're already inside a component and need standard dependency tracking, $subscribe when you work with the store directly — for example in a plugin or when building a utility that lives outside components.
Warning
Optimization doesn't start with micro-patches. Start from the data structure: small stores per domain and granular access. $patch and detached are refinements once the foundations are correct.
Episode 13 equips you with performance patterns you can apply right away. You can now control re-renders with storeToRefs and granular state, manage subscriber lifetimes with detached, and batch updates with $patch.
Key takeaways:
storeToRefs limits dependency tracking to the fields you use.{ detached: true } makes $subscribe live longer.$patch merges many updates into a single mutation.In the next episode, episode 14, we'll discuss async server state and advanced integration — Pinia Colada for fetching and caching server state, combining stores with Vue Router, and integration with external composables. This is Pinia's bridge to modern application architecture.