In Vue 3’s Composition API, most of your component’s logic goes into the setup() function.It receives props as its first argument, which is a reactive object.
But a common pitfall for new users is that if you directly destructure props inside setup(), the destructured variables will lose their reactivity. This means they won’t update when the parent component changes the prop’s value.
Understanding the Reactivity Loss
When you receive props in setup(), Vue gives it as a reactive Proxy object. This Proxy is how Vue tracks changes to the props.
If you do this:
// Inside setup()const { myProp } = props;
Destructuring myProp in setup() breaks its reactivity. It becomes a plain value, so updates from the parent won’t change it or trigger re-renders in your component.
Also Read: How to Listen for ‘Props’ Changes
How to Maintain Reactivity of Props in setup()?
There are a few key ways to ensure your props remain reactive within setup():
1. Access Props Directly from the props Object
This is the simplest way. Just refer to the prop as props.propName everywhere you need it.
// ChildComponent.vue<script setup>
import { defineProps } from ‘vue’;
const props = defineProps([‘title’]);
</script>
<template>
<h1>{{ props.title }}</h1> </template>
2. Use toRefs() for Destructured Props
If you want to destructure props but keep them reactive, wrap them with toRefs(). This converts each prop into a separate ref.
// ChildComponent.vue<script setup>
import { defineProps, toRefs } from ‘vue’;
const props = defineProps([‘message’]);
const { message } = toRefs(props); // ‘message’ is now a reactive ref
</script>
<template>
<p>{{ message }}</p>
</template>
Also Read: Why Enterprises are Choosing Vue.js for their Large-Scale Projects?
3. Use toRef() for a Single Prop
If you only need one specific prop as a reactive ref, toRef() is a concise option.
// ChildComponent.vue<script setup>
import { defineProps, toRef } from ‘vue’;
const props = defineProps([‘title’]);
const title = toRef(props, ‘title’); // ‘title’ is a reactive ref
</script>
<template>
<h1>{{ title }}</h1> </template>
4. Use a computed Property for Derived Values
If you need to calculate or transform a prop, wrap the logic in a computed so it stays reactive.
// ChildComponent.vue<script setup>
import { defineProps, computed } from ‘vue’;
const props = defineProps([‘price’, ‘quantity’]);
const totalPrice = computed(() => props.price * props.quantity); // Reactive!
</script>
<template>
<p>Total: {{ totalPrice }}</p> </template>
Also Read: Vue JS Loop Via v-for X Times (in a range)
Key Takeaway
If you destructure props directly in setup(), they lose reactivity. To keep them reactive, use props directly, wrap them with toRefs() or toRef(), or use a computed for derived values. This way your component will update correctly when props change.