Het probleem: props drilling
Stel je hebt een gebruiker-object in App.vue dat je nodig hebt in een diep genest UserBadge component:
App.vue (user hier)
└─ Layout.vue
└─ Sidebar.vue
└─ ProfileMenu.vue
└─ UserBadge.vue ← user hier nodig
Zonder hulp moet je user als prop doorgeven via elk tussencomponent. Dat heet props drilling — vermoeiend en foutgevoelig.
Met provide / inject kan een parent data direct naar een (eventueel diep geneste) child sturen, zonder tussencomponenten.
provide() & inject()
In de parent: provide
<!-- src/App.vue -->
<script setup>
import { provide } from 'vue'
const user = { name: 'Anna', role: 'admin' }
provide('user', user) // sleutel + waarde
</script>
In het (diep geneste) child: inject
<!-- src/components/UserBadge.vue (kan diep genest zitten) -->
<script setup>
import { inject } from 'vue'
const user = inject('user')
</script>
<template>
<span>{{ user.name }} ({{ user.role }})</span>
</template>
Wat is er gebeurd? De parent zegt: "hier is een waarde onder de naam 'user'". Elke (sub-)child kan die opvragen met inject('user') — ongeacht hoe diep hij ligt.
Default value voor inject
Als de parent niets provide't, geef je een fallback mee:
const user = inject('user', { name: 'Gast' }) // default
Reactivity bewaren — provide met een ref
Wil je dat de child meegroeit met wijzigingen in de parent? Provide een ref, niet een gewone waarde.
<!-- src/App.vue -->
<script setup>
import { ref, provide } from 'vue'
const user = ref({ name: 'Anna', role: 'admin' })
provide('user', user) // ref doorgeven
const promote = () => {
user.value.role = 'super-admin'
// Alle componenten die 'user' injecten, zien de wijziging direct!
}
</script>
<!-- src/components/UserBadge.vue -->
<script setup>
import { inject } from 'vue'
const user = inject('user')
</script>
<template>
<span>{{ user.name }} ({{ user.role }})</span>
<!-- updatet automatisch als parent user.value muteert -->
</template>
Let op: Provide je een gewone const user = {...} (geen ref)? Dan is het niet reactive. Wijzigingen in de parent komen niet door in de children.
Compleet voorbeeld — dark mode toggle
De klassieke use case: een dark/light theme dat overal in de app beschikbaar moet zijn.
App.vue — provide
<script setup>
import { ref, provide } from 'vue'
import Layout from './components/Layout.vue'
const isDark = ref(false)
const toggleTheme = () => {
isDark.value = !isDark.value
}
// Provide de state EN de toggle-functie
provide('theme', { isDark, toggleTheme })
</script>
<template>
<div :class="{ dark: isDark }">
<Layout />
</div>
</template>
ThemeToggle.vue — diep genest, gebruikt het
<script setup>
import { inject } from 'vue'
const { isDark, toggleTheme } = inject('theme')
</script>
<template>
<button @click="toggleTheme">
{{ isDark ? '☀️ Light mode' : '🌙 Dark mode' }}
</button>
</template>
Wat heb je nu? Vanuit elk component (hoe diep ook) kan je het thema lezen en wisselen. Geen props doorgeven, geen events naar boven sturen.
Provide/inject of Pinia?
Allebei werken voor "state delen tussen componenten." Wanneer kies je wat?
| Provide/Inject | Pinia | |
|---|---|---|
| Bereik | Alleen child-tree | Globaal (hele app) |
| Setup | Geen install nodig | npm install pinia |
| Devtools | Beperkt | Volledig zichtbaar |
| Beste voor | Theme, layout-config, lokale tree | Auth, user data, cart, alles globaal |
Vuistregel:
- Iets dat in je hele app speelt (ingelogde gebruiker, winkelmandje) → Pinia
- Iets dat alleen binnen één sectie speelt (layout-config, modal-context) → provide/inject
Belangrijke Regels
provide('key', value)in de parentinject('key')in het child — werkt op elk diep level- Voor reactive data: provide een
ref, geen gewone waarde - Key kan een string zijn — kies een unieke, beschrijvende naam
- Voor globale state: gebruik Pinia, niet provide/inject
Veelgemaakte Fouten
Fout — gewone waarde provide'n, verwachten dat het reactive is:
// Parent
let user = { name: 'Anna' }
provide('user', user)
user.name = 'Bram' // ❌ child ziet dit niet
Goed — provide een ref:
const user = ref({ name: 'Anna' })
provide('user', user)
user.value.name = 'Bram' // ✅ child updatet
Fout — key mismatch:
// Parent
provide('currentUser', user)
// Child
const user = inject('user') // ❌ andere naam, krijgt undefined
Goed:
const user = inject('currentUser') // ✅ zelfde key