Introduction

Vue slots allow parent components to inject content into child components. When slot content depends on parent state but does not update when that state changes, the issue is usually related to reactivity tracking, scoped slot bindings, or the compilation scope of slot content:

html
<!-- Parent template -->
<ChildComponent>
    <p>Count: {{ count }}</p> <!-- This may not update if reactivity is broken -->
</ChildComponent>

The slot content is compiled in the parent's scope, but if the child component controls rendering, the parent's reactive updates may not trigger a re-render of the slot.

Symptoms

  • Slot content displays initial state but does not update when parent data changes
  • Scoped slot variables show stale values
  • Computed properties in slot content do not re-evaluate
  • v-if conditions in slots do not respond to parent state changes
  • Slot content works in simple cases but fails in nested component scenarios

Common Causes

  • Slot content references a non-reactive variable
  • Child component uses v-once or shallow-render which prevents slot updates
  • Scoped slot slot-props are not reactive (passed as plain values)
  • Parent state change does not trigger child re-render due to shouldComponentUpdate optimization
  • Using v-slot syntax incorrectly, breaking the reactive binding

Step-by-Step Fix

  1. 1.Ensure the parent data is reactive:
  2. 2.```javascript
  3. 3.// Parent component
  4. 4.export default {
  5. 5.data() {
  6. 6.return {
  7. 7.count: 0, // This IS reactive
  8. 8.};
  9. 9.},
  10. 10.};
  11. 11.`
  12. 12.Use scoped slots for child-to-parent data sharing:
  13. 13.```html
  14. 14.<!-- ChildComponent.vue -->
  15. 15.<template>
  16. 16.<div>
  17. 17.<slot :item="currentItem" :is-active="isActive"></slot>
  18. 18.</div>
  19. 19.</template>

<!-- Parent usage --> <ChildComponent v-slot="{ item, isActive }"> <span>{{ item.name }} - {{ isActive ? 'Active' : 'Inactive' }}</span> </ChildComponent> ```

  1. 1.Use a key to force slot re-render when state changes:
  2. 2.```html
  3. 3.<ChildComponent :key="stateVersion">
  4. 4.<p>Count: {{ count }}</p>
  5. 5.</ChildComponent>
  6. 6.`
  7. 7.Increment stateVersion when you need to force the slot to re-render:
  8. 8.```javascript
  9. 9.this.stateVersion++;
  10. 10.`
  11. 11.Pass reactive props alongside slot content:
  12. 12.```html
  13. 13.<ChildComponent :data="reactiveData">
  14. 14.<template v-slot:default="{ localData }">
  15. 15.<!-- Both parent and child reactive data available -->
  16. 16.<p>Parent: {{ count }} | Child: {{ localData.value }}</p>
  17. 17.</template>
  18. 18.</ChildComponent>
  19. 19.`
  20. 20.In Vue 3, use the v-slot shorthand and ensure reactivity:
  21. 21.```html
  22. 22.<ChildComponent>
  23. 23.<template #default="{ user }">
  24. 24.<!-- Vue 3's reactivity system handles this correctly -->
  25. 25.<UserCard :user="user" :total="computedTotal" />
  26. 26.</template>
  27. 27.</ChildComponent>
  28. 28.`

Prevention

  • Always use reactive data properties (data(), ref(), reactive()) for slot content bindings
  • Use scoped slots when the child component needs to pass data to the slot content
  • Avoid v-once on components that contain dynamic slot content
  • Test slot content updates in unit tests by changing parent state and asserting slot output
  • Use Vue DevTools to inspect the reactive dependency chain for slot content
  • Document the expected reactivity behavior of slot content in component documentation
  • In Vue 3, leverage the improved reactivity system but still test slot update behavior