Vue.js 3 — How to Destructure Props in Composition API

Vue 3 introduced the composition API using <script setup> blocks. You can write any JavaScript in there to set up your Vue components. The composition API also introduced some hiccups related to reactive properties.

For example, it’s possible to lose reactivity when destructuring reactive objects, like props. Vue’s ESLint plugin warns you about this issue when destructuring props in your <script setup>. You’ll see an ESLint error like this:

Getting a value from the props in root scope of <script setup> will cause the value to lose reactivity.

— ESLint feat. Vue plugin (when destructuring props)

This tutorial shows you how to properly destructure props in Vue’s composition API and keep reactivity.

Vue.js 3 Series Overview

Destructure Vue Props in the Composition API

Here’s the problem: Vue loses reactivity of props, especially of type-only prop declarations (because there’s no value reference Vue could bind to):

<script setup lang="ts">  
const props = defineProps<{ name: string }>()

// 🚨 don’t do this: `name` is not reactive anymore and changes from the parent don’t propagate
const { name } = props  
</script>  

Use the props Property (and Computed Values)

A solution is to always use the props.x property accessor without shortcutting variable access. This keeps reactivity and inside your templates you can access props directly:

<script setup lang="ts">  
import { computed } from 'vue'

const props = defineProps<{ name: string }>()

// ✅ do this: access props using `props.name`
const greeting = computed(() => {  
  return 'Hello ' + props.name ?? 'unknown'
})
</script>

<template>  
  <p>
    My name is: {{ name }}
    {{ greeting }}
  </p>
</template>  

The name and greeting variables change whenever the props.name value changes.

Use Vue’s toRefs Helper

There’s another way to keep props reactive: translating them to refs using the toRefs helper. There’s also a toRef helper in Vue for a single value. We’re using the plural version toRefs here because props are always an object:

<script setup lang="ts">  
import { computed, toRefs } from 'vue'

const props = defineProps<{ name: string }>()

// ✅ do this: `name` stays reactive and updates accordingly when changed in the parent component
const { name } = toRefs(props)

const greeting = computed(() => {  
  return 'Hello ' + name.value ?? 'unknown'
})
</script>

<template>  
  <p>
    My name is: {{ name }}
    {{ greeting }}
  </p>
</template>  

You can destructure the resulting objects after using toRefs, but every variable is a ref then. You must access the value using the name.value notation.

Spread Props into a reactive Object

Another solution is to use JavaScript’s spread operator to create a new, reactive object based on the passed props. From there you could destructure your desired properties:

<script setup lang="ts">  
import { reactive } from 'vue'

const props = defineProps<{ name: string }>()

// ✅ do this: get the `name` from the reactive object
const { name = 'Future Studio' } = reactive({ ...props })  
</script>

<template>  
  <p>
    My name is: {{ name }}
  </p>
</template>  

Destructure Vue Props with Vite

If you’re using Vite to process and bundle your Vue.js app, you may use a Vite compile-time option. Since Vue.js 3.3, you can opt-in to the destructureProps: true configuration. This option enables prop destructuring and the assignment of default values to props inside your <script setup> block.

Here’s the Vite configuration you need to apply for prop destructuring:

/**
 * @see https://vitejs.dev/config/
 */
export default defineConfig({  
  plugins: [
    vue({
      script: {
        propsDestructure: true,
      }
    })
  ]
})

Notice: you may need to adjust your ESLint configuration to accept destructured props in the root of your <script setup> block:

{
  "rules": {
    "vue/no-setup-props-destructure": "off"
  }
}

From here, you can destructure props inside your <script setup> block and also assign default values:

<script setup lang="ts">  
const { name = 'Future Studio' } = defineProps<{ name?: string }>()  
</script>

<template>  
  <!-- displays "Name: Future Studio" -->
  <p>
    Name: {{ name }}
  </p>
</template>  

Enjoy destructuring props in Vue.js!


Mentioned Resources

Explore the Library

Find interesting tutorials and solutions for your problems.