Lifting state works great until your component tree gets deep. Then you encounter prop drilling.
What is Prop Drilling?
Passing props through multiple levels of components that don't use them:
svelte<!-- App.svelte --> <script> let user = $state({ name: 'Alice', role: 'admin' }); </script> <Layout {user}> <Content /> </Layout>
svelte<!-- Layout.svelte --> <script> let { user, children } = $props(); // Layout doesn't use 'user', just passes it down </script> <Header {user} /> <main>{@render children()}</main> <Footer />
svelte<!-- Header.svelte --> <script> let { user } = $props(); // Header doesn't use 'user' either! </script> <nav> <Logo /> <Navigation /> <UserMenu {user} /> <!-- Finally used here! --> </nav>
svelte<!-- UserMenu.svelte --> <script> let { user } = $props(); // This is where we actually need user! </script> <div>Welcome, {user.name}!</div>
The Problems with Prop Drilling
1. Verbose and Repetitive
App ā Layout ā Header ā UserMenu
(pass) (pass) (finally use!)
2. Tight Coupling
Every intermediate component must know about user even if they don't use it.
3. Refactoring Nightmare Adding a new prop means updating every component in the chain.
4. Type Noise Every component's props type includes things it doesn't care about.
Signs You Need a Different Solution
- Props passing through 3+ levels unused
- Many components getting "pass-through" props
- Difficulty refactoring prop shapes
- Components becoming less reusable due to prop requirements
Solutions Preview
We'll cover these in upcoming lessons:
- Context API - For tree-scoped state
- Global stores - For app-wide state (client-only)
- URL state - For shareable state