Solving Prop Drilling in Svelte with Writable and Derived Stores
Stop passing props through five layers of components. Learn how to use Svelte's writable and derived stores to manage global state efficiently and prevent memory leaks.
08 Dec 2025, 09:26 UTC

The Prop Drilling Bottleneck
When building a Svelte application, you eventually hit a wall where a piece of state—like a user's authentication status or a shopping cart total—is needed by a component deep in the UI tree. The standard approach is to pass that data down through every intermediate component via props. This is known as "prop drilling."
Prop drilling makes components fragile. If you change the shape of the data at the top, you must manually update every component in the chain, even those that don't actually use the data but merely pass it along. Svelte Stores solve this by moving state into a standalone object that any component can subscribe to directly, regardless of its position in the hierarchy.
Writable Stores: The Source of Truth
A writable store is an object that holds a value and allows any part of the application to change that value. It follows a simple contract: it must have a subscribe method, and for writable stores, set and update methods.
- set(): Replaces the store value entirely.
- update(): Takes a callback function that receives the current value and returns a new one. This is essential for state that depends on its previous value (like counters or toggles).
Derived Stores: Reactive Transformations
Often, you don't need a new piece of independent state, but rather a value that is a transformation of existing state. For example, if you have a store for itemsInCart, you might need a second store for cartTotal.
A derived store takes one or more existing stores and a callback. Whenever the source stores change, the derived store automatically recalculates. This prevents "syncing" logic from cluttering your components and ensures the transformation logic lives in one place.
Implementation Example: A Simple Cart System
To implement this, create a separate JavaScript file (e.g., stores.js) to hold your state. This ensures the store is a singleton shared across the app.
// stores.js
import { writable, derived } from 'svelte/store';
// The source of truth: a list of products
export const cart = writable([]);
// A derived store that calculates the total price
export const cartTotal = derived(cart, ($cart) => {
return $cart.reduce((sum, item) => sum + item.price, 0);
});
// A custom store wrapper to encapsulate logic
export const cartActions = {
addItem: (product) => cart.update(items => [...items, product]),
clearCart: () => cart.set([])
};
In your Svelte components, use the $ prefix to access the store value. This is a Svelte shorthand that handles the subscription and, crucially, the unsubscription when the component is destroyed to prevent memory leaks.
<script>
import { cart, cartTotal, cartActions } from './stores.js';
function handleAdd() {
cartActions.addItem({ name: 'Technical Guide', price: 20 });
}
</script>
<div>
<p>Items in cart: {cart.length}</p>
<p>Total: ${$cartTotal}</p>
<button on:click={handleAdd}>Add Item</button>
<button on:click={cartActions.clearCart}>Clear</button>
</div>
Verification and Risks
To verify the implementation, check the browser's network or console logs to ensure the cartTotal updates immediately upon calling addItem. Note that you must use the $ prefix (e.g., $cartTotal) to read the value reactively; referencing cartTotal without the prefix refers to the store object itself, not the value inside it.
Trade-offs and Limitations
While stores eliminate prop drilling, they introduce a different risk: global state sprawl. When any component can modify a store, it becomes harder to track which part of the app triggered a state change, making debugging more difficult.
Additionally, be cautious of circular dependencies. If Store A is derived from Store B, and Store B is derived from Store A, Svelte will throw a runtime error. Always maintain a unidirectional flow of data: Writable Store → Derived Store → UI.
Actionable Summary
Use stores when state is needed by three or more non-adjacent components. Start with a writable store for your raw data, use derived stores for any calculated views of that data, and always use the $ prefix in components to ensure automatic memory management.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.