Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Vue 3 debugging reference for reactivity issues, computed errors, watcher bugs, async failures, and SSR hydration problems.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
reference/provide-inject-reactivity-not-automatic.md
1---2title: Provide/Inject Values Are Not Reactive by Default3impact: HIGH4impactDescription: Provided primitive values lose reactivity, causing injecting components to not update when the source value changes5type: gotcha6tags: [vue3, provide-inject, reactivity, composition-api, options-api]7---89# Provide/Inject Values Are Not Reactive by Default1011**Impact: HIGH** - A common misconception is that provide/inject automatically maintains reactivity. By default, provided primitive values are NOT reactive. If the provided value changes in the provider, injecting components will NOT be updated.1213## Task Checklist1415- [ ] Always wrap primitive values in `ref()` before providing16- [ ] Use `computed()` in Options API `provide()` for reactive data17- [ ] Never destructure refs when providing - pass the ref directly18- [ ] Understand that provided refs are NOT auto-unwrapped in injectors1920## The Gotcha: Primitives Lose Reactivity2122**Wrong - Primitive loses reactivity:**23```vue24<!-- Provider.vue -->25<script setup>26import { ref, provide } from 'vue'2728const count = ref(0)2930// WRONG: Providing the unwrapped value loses reactivity31provide('count', count.value) // Provides 0, not a reactive value3233function increment() {34count.value++ // Injector will NOT see this change35}36</script>37```3839```vue40<!-- Injector.vue -->41<script setup>42import { inject } from 'vue'4344const count = inject('count') // Gets 0, forever static45</script>4647<template>48<!-- This will always show 0 -->49<div>Count: {{ count }}</div>50</template>51```5253**Correct - Provide the ref itself:**54```vue55<!-- Provider.vue -->56<script setup>57import { ref, provide } from 'vue'5859const count = ref(0)6061// CORRECT: Provide the ref, not the value62provide('count', count)6364function increment() {65count.value++ // Injector WILL see this change66}67</script>68```6970```vue71<!-- Injector.vue -->72<script setup>73import { inject } from 'vue'7475// The ref is injected as-is, maintaining reactivity76const count = inject('count')77</script>7879<template>80<!-- Access .value in script, auto-unwrapped in template -->81<div>Count: {{ count }}</div>82</template>83```8485## Options API: Use computed() for Reactivity8687In Options API, the `provide` option with plain properties is NOT reactive:8889**Wrong - Options API without computed:**90```js91export default {92data() {93return {94message: 'Hello'95}96},97// WRONG: This is NOT reactive98provide() {99return {100message: this.message // Provides 'Hello' as a static string101}102}103}104```105106**Correct - Use computed() in Options API:**107```js108import { computed } from 'vue'109110export default {111data() {112return {113message: 'Hello'114}115},116provide() {117return {118// CORRECT: Wrap in computed for reactivity119message: computed(() => this.message)120}121}122}123```124125## Understanding Ref Behavior in Inject126127When you provide a ref, it is injected as-is and NOT auto-unwrapped:128129```vue130<!-- Provider.vue -->131<script setup>132import { ref, provide } from 'vue'133134const user = ref({ name: 'John' })135provide('user', user)136</script>137```138139```vue140<!-- Injector.vue -->141<script setup>142import { inject } from 'vue'143144const user = inject('user')145146// In script, access with .value147console.log(user.value.name) // 'John'148149function updateName(newName) {150user.value.name = newName // Works, but mutations should be in provider151}152</script>153154<template>155<!-- In template, auto-unwrapped at top level -->156<div>{{ user.name }}</div>157</template>158```159160## Providing Reactive Objects161162Reactive objects (created with `reactive()`) maintain reactivity when provided:163164```vue165<!-- Provider.vue -->166<script setup>167import { reactive, provide } from 'vue'168169const state = reactive({170count: 0,171message: 'Hello'172})173174provide('state', state)175</script>176```177178```vue179<!-- Injector.vue -->180<script setup>181import { inject } from 'vue'182183const state = inject('state')184// state.count and state.message are reactive185</script>186```187188## Common Mistake: Destructuring Breaks Reactivity189190**Wrong - Destructuring provided reactive state:**191```vue192<script setup>193import { inject } from 'vue'194195// WRONG: Destructuring loses reactivity196const { count, message } = inject('state')197// count and message are now static values198</script>199```200201**Correct - Keep the reference intact:**202```vue203<script setup>204import { inject, toRefs } from 'vue'205206const state = inject('state')207// Use state.count and state.message directly208209// Or use toRefs if you need destructured reactive refs210const { count, message } = toRefs(state)211</script>212```213214## Debugging Tip215216If your injected value isn't updating:2172181. Check if you provided `ref.value` instead of `ref`2192. Check if you destructured a reactive object2203. In Options API, ensure you used `computed()`2214. Use Vue DevTools to inspect the provided values222223## Reference224- [Vue.js Provide/Inject - Working with Reactivity](https://vuejs.org/guide/components/provide-inject.html#working-with-reactivity)225- [How to make provide/inject reactive - LogRocket Blog](https://blog.logrocket.com/how-to-make-provide-inject-reactive/)226- [GitHub Issue: Inject/Provide is not reactive](https://github.com/vuejs/vue/issues/7017)227