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

vue
<script setup>
const theme = inject('theme', ref('light')); // Default is a ref
</script>