The Problem
Vue's provide and inject allow ancestor components to share data with descendants. But if you provide a plain value (not a ref or reactive object), changes to the provided value are not reactive in injected components.
Symptoms
- Injected value never updates when ancestor changes it
- Works with props but not with provide/inject
- Deeply nested components show stale data
- No error in console, silent reactivity loss
Real Error Scenario
```vue <!-- Ancestor.vue --> <script setup> const theme = ref('light');
// WRONG: Providing the raw value, not the ref provide('theme', theme.value); // Provides 'light' as a plain string
function toggleTheme() { theme.value = theme.value === 'light' ? 'dark' : 'light'; // Injected components never see this change! } </script>
<!-- Descendant.vue (deeply nested) --> <script setup> const theme = inject('theme'); // Gets plain string 'light' </script>
<template> <div :class="theme">Content</div> <!-- Never changes class --> </template> ```
How to Fix It
Fix 1: Provide the ref Directly
```vue <script setup> const theme = ref('light');
// CORRECT: Provide the ref itself provide('theme', theme); </script> ```
```vue <!-- Descendant --> <script setup> const theme = inject('theme'); </script>
<template> <div :class="theme.value">Content</div> </template> ```
Fix 2: Use computed for Read-Only Reactive Data
```vue <!-- Ancestor --> <script setup> const theme = ref('light'); provide('theme', computed(() => theme.value)); </script>
<!-- Descendant --> <script setup> const theme = inject('theme'); // theme is a computed, auto-unwrapped in template </script>
<template> <div :class="theme">Content</div> <!-- Works! --> </template> ```
Fix 3: Provide Reactive Object
```vue <!-- Ancestor --> <script setup> const config = reactive({ theme: 'light', lang: 'en', notifications: true }); provide('config', config); </script>
<!-- Descendant --> <script setup> const config = inject('config'); </script>
<template> <div :class="config.theme">Content in {{ config.lang }}</div> </template> ```
Fix 4: Default Value with Reactivity
<script setup>
const theme = inject('theme', ref('light')); // Default is a ref
</script>