Vue Component Props

By

Learn how Vue 3 props pass data from parent to child, including defineProps, types, defaults, required props, and camelCase vs kebab-case in templates.

~~~

Define a prop inside the component

Props are the way components accept data from the components that include them (the parent components).

A component must declare the props it expects. In a Single File Component with <script setup> you use defineProps, the usual Vue 3 style:

<template>
  <p>Hi {{ name }}</p>
</template>

<script setup>
defineProps(['name'])
</script>

With the Options API:

<template>
  <p>Hi {{ name }}</p>
</template>

<script>
export default {
  props: ['name']
}
</script>

Or when you register a component on the app with an inline template:

import { createApp } from 'vue'

const app = createApp({})

app.component('UserName', {
  props: ['name'],
  template: '<p>Hi {{ name }}</p>'
})

app.mount('#app')

Accept multiple props

List every prop you expect. From here on, the defineProps snippets go inside <script setup>, and the export default ones are the Options API equivalent:

defineProps(['firstName', 'lastName'])
export default {
  props: ['firstName', 'lastName']
}

Set the prop type

Use an object form to declare types:

defineProps({
  firstName: String,
  lastName: String
})

Valid types:

In development, Vue warns when the runtime type does not match.

Allow more than one type:

defineProps({
  firstName: [String, Number]
})

Set a prop to be mandatory

defineProps({
  firstName: {
    type: String,
    required: true
  }
})

Set the default value of a prop

defineProps({
  firstName: {
    type: String,
    default: 'Unknown person'
  }
})

For objects and arrays, default must be a function that returns the value. Otherwise every instance of the component would share the same object:

defineProps({
  name: {
    type: Object,
    default() {
      return {
        firstName: 'Unknown',
        lastName: ''
      }
    }
  }
})

Since Vue 3.5 there is a shorter way in <script setup>. You destructure the props and use a plain JavaScript default value:

const { firstName = 'Unknown person' } = defineProps(['firstName'])

firstName stays reactive, the compiler takes care of that.

You can also write a custom validator, which is handy for complex data:

defineProps({
  name: {
    validator(value) {
      return value === 'Flavio'
    }
  }
})

Passing props to the component

Static string:

<UserName name="Flavio" />

From parent state, use v-bind / ::

<template>
  <UserName :name="name" />
</template>

<script setup>
import { ref } from 'vue'
import UserName from './UserName.vue'

const name = ref('Flavio')
</script>

In JavaScript you declare prop names in camelCase (firstName). In the template you can write the attribute in kebab-case (first-name), like a normal HTML attribute, and Vue maps one to the other:

<UserName first-name="Flavio" last-name="Copes" />
defineProps({
  firstName: String,
  lastName: String
})

Inside a .vue file firstName="Flavio" works too. In a template written directly in the HTML page, kebab-case is mandatory, because the browser lowercases attribute names before Vue sees them.

Props are one-way, from parent to child. When the parent updates the prop, the child sees the new value. Do not change a prop inside the child. If the child needs a change, emit an event to the parent, or copy the value into local state. More on that in component communication.

You can use an expression when binding:

<ColorBox :colored="color === 'white'" />
Tagged: Vue.js · All topics

Want me to talk about your product? You can sponsor this site.

~~~

Related posts about vue: