Vue.js 3 Component Props Tutorial

In this Vue tutorial we learn how to pass data from one component to another down the tree with props.

We cover the steps to create and use props, dynamic & default values, prop types, non-prop attributes and how to specify that a prop is required.

Lesson Project

If you want to follow along with the examples, you will need an app generated by the Vue CLI as well as the following extra component.

  • /src/components/GreetingMessage.vue

The project should look similar to the following.

Example: project
project-folder/
├── src/
|   ├── components/
|   |   └── GreetingMessage.vue
|   └── App.vue

The root App component imports and uses the GreetingMessage component.

Example: src/App.vue
<template>
  <greeting-message />
</template>

<script>
import GreetingMessage from './components/GreetingMessage'

export default {
  components: { GreetingMessage }
}
</script>

The GreetingMessage component can just show a simple message in a paragraph to start with.

Example: src/components/GreetingMessage.vue
<template>
  <p>Hello, World!</p>
</template>

What are component props

Props allow us to pass data from a parent component down to a child component through custom attributes on the parent component’s HTML tag.

note Props are only used for parent to child communication. If we want to communicate from the child to the parent, we use Custom Component Events .

How to create and use a prop

We create a prop in a simple three step process.

  1. Step 1: Add a custom attribute on the component’s HTML tag in the parent component.
  2. Step 2: Register the prop in the child component.
  3. Step 3: Use the registered prop.

Our example use the root App component as the parent and GreetingMessage as the child.

Step 1: Add a custom attribute on the HTML tag

To create a prop, we add an attribute to the child component’s instance in the parent’s template block.

Syntax: parent prop attribute
<template>
  <child-component propName="propValue" />
</template>

As an example, we’ll create 4 instances of GreetingMessage in the root App component and add a name prop with a different value for each.

Example: src/App.vue
<template>
  <greeting-message name="John" />
  <greeting-message name="Jane" />
  <greeting-message name="Jack" />
  <greeting-message name="Jill" />
</template>

<script>
import GreetingMessage from './components/GreetingMessage'

export default {
  components: { GreetingMessage }
}
</script>

Step 2: Register the prop in the child component

Just adding a prop to the tag in the parent component is not enough. We need to register the prop so that Vue knows to expect a value from it.

To register a prop, we add it to an array in the props option of the child component’s config object. The name must be a string value.

Syntax: props option
<script>
export default {
  props: ['propName']
}
</script>

For our example, we’ll add the name prop we defined to the props array in GreetingMessage .

Example: src/components/GreetingMessage.vue
<template>
  <p>Hello, World!</p>
</template>

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

Step 3: Use the prop in the child component

Now that the prop has been registered and Vue knows about it, we can use it. We can perform operations on the prop value just like we would with a data property, or we can output it in the template.

As an example, we’ll just output our name prop in the paragraph in the template.

Example: src/components/GreetingMessage.vue
<template>
  <p>Hello, {{ name }}!</p>
</template>

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

If we run the example in the browser, we should see 4 paragraphs that greet the different names.

Output:
Hello, John!

Hello, Jane!

Hello, Jack!

Hello, Jill!

How to use multiple props on a single element

Vue allows us to use as many props on an element as we need. There is nothing special we need to do, we just add more props.

Syntax: multiple props
<template>
  <component
    prop1="value1"
    prop2="value2"
    prop3="value3"
  />
</template>

To demonstrate, let’s add a lastName prop to the root App component of our previous example.

Example: src/App.vue
<template>
  <greeting-message name="John" lastName="Doe" />
  <greeting-message name="Jane" lastName="Doe" />
</template>

<script>
import GreetingMessage from './components/GreetingMessage'

export default {
  components: { GreetingMessage }
}
</script>

Then register and use the prop in GreetingMessage .

Example: src/components/GreetingMessage.vue
<template>
  <p>Hello, {{ name }} {{ lastName }}!</p>
</template>

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

If we save and take a look at the browser, we see the new prop added to the output.

Output:
Hello, John Doe!

Hello, Jane Doe!

Multi-word prop name casing

If a prop name has multiple words, we can use kebab-case. Just like component names, we don’t have to change the prop name because Vue will automatically convert it.

Syntax: multi-word kebab-case
<template>
  <!-- propName or PropName becomes -->
  <component prop-name="value" />
</template>

To demonstrate, let’s change our example to use last-name instead of lastName .

Example: src/App.vue
<template>
  <greeting-message name="John" last-name="Doe" />
  <greeting-message name="Jane" last-name="Doe" />
</template>

<script>
import GreetingMessage from './components/GreetingMessage'

export default {
  components: { GreetingMessage }
}
</script>

The convention is to use kebab-case for attributes in the HTML tag so we will do it this way from now on.

How to assign dynamic values to props with v-bind

At the moment we pass static values into our props, but we can also use the v-bind directive to bind dynamic data. That’s to say, we can bind data properties or computed properties to props on a component.

To do this, we bind to the prop and specify the data or computed property as the value.

Syntax: dynamic prop values
<template>
  <!-- data property with v-bind -->
  <component v-bind:prop-name="dataName" />

  <!-- computed property with v-bind shorthand -->
  <component :prop-name="computedName" />
</template>

<script>
export default {
  data() {
    return: { dataName: value }
  },
  computed: {
    computedName() {}
  }
}
</script>

As the first example, we’ll add two data properties and bind them to a second component instance.

Example: src/App.vue
<template>
  <!-- static -->
  <greeting-message name="John" last-name="Doe" />

  <!-- dynamic with data properties -->
  <greeting-message :name="fName" :last-name="lName" />
</template>

<script>
import GreetingMessage from './components/GreetingMessage'

export default {
  components: { GreetingMessage },
  data() {
    return {
      fName: 'Jane',
      lName: 'Doe'
    }
  }
}
</script>

If we run the example in the browser, we should see a greeting for both names.

Output:
Hello, John Doe!

Hello, Jane Doe!

As the second example, we’ll replace the two props with a new one for a full name. Then we’ll create a computed property that combines the data properties and bind it to the component.

Example: src/App.vue
<template>
  <greeting-message :full-name="fullName" />
</template>

<script>
import GreetingMessage from './components/GreetingMessage'

export default {
  components: { GreetingMessage },
  data() {
    return {
      fName: 'Jane',
      lName: 'Doe'
    }
  },
  computed: {
    fullName() {
      return this.fName + ' ' + this.lName
    }
  }
}
</script>

In the child component we’ll replace the two previous props with the new fullName prop.

Example: src/components/GreetingMessage.vue
<template>
  <p>Hello, {{ fullName }}!</p>
</template>

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

If we run the example in the browser, we should see a greeting for name in the computed property.

Output:
Hello, Jane Doe!

How to specify a soft prop type

When we’re working with others on a project, it’s useful to specify the type a value a prop needs so that other developers can see at a glance what type of data the prop can accept.

To do this, we change the props option in the child component from an array to an object. Then we specify the prop name and type as a key:value pair.

Syntax:
// change from an array
props: ['prop_name']

// to an object
props: {
  prop_name: prop_type
}

To demonstrate, let’s modify the fullName prop from the previous example to have a type.

Example: src/components/GreetingMessage.vue
<template>
  <p>Hello, {{ fullName }}!</p>
</template>

<script>
export default {
  props: {
    fullName: String
  }
}
</script>

If we save and take a look at the browser, we see that the example still displays the full name.

Let’s change the type to a Number and see what happens.

Example: src/components/GreetingMessage.vue
<template>
  <p>Hello, {{ fullName }}!</p>
</template>

<script>
export default {
  props: {
    fullName: Number
  }
}
</script>

If we take a look in the browser, everything still works even though the type is different.

That’s because Vue doesn’t perform a strong type check. That’s to say, the compilation won’t fail if the wrong type is used. Vue will only raise a warning if the type doesn’t match.

If we open up the browser’s developer tools and navigate to the Console tab, we will see the following warning message.

Output:
[Vue warn]: Invalid prop: type check failed for prop "fullName".
  Expected Number with value NaN, got String with value "Jane Doe".

The type feature mostly serves as a documentation of sorts for developer(s) to easily see what type of value we intend for the prop.

How to specify if a prop is required

Vue allows us to make a prop required by setting the required prop option to true.

To use extra options, we convert our prop to an object. In the object we specify options like type and required as key:value pairs.

Syntax: prop option required
// change from
props: {
  prop_name: prop_type
}

// to a nested object
props: {
  prop_name: {
    type: prop_type,
    required: true/false
  }
}

To demonstrate, let’s modify the child component in our previous example and add the required option.

Example: src/components/GreetingMessage.vue
<template>
  <p>Hello, {{ fullName }}!</p>
</template>

<script>
export default {
  props: {
    fullName: {
      type: String,
      required: true
    }
  }
}
</script>

As with the type, the required option is a soft check and will not produce a compilation error but rather raise a warning in the browser’s console.

To demonstrate, we’ll remove everything related to the prop in the parent component.

Example: src/App.vue
<template>
  <greeting-message />
</template>

<script>
import GreetingMessage from './components/GreetingMessage'

export default {
  components: { GreetingMessage }
}
</script>

When we save and take a look at the browser, only the hardcoded values are displayed.

Output:
Hello, !

And in the Console, we’ll see the following warning.

Output:
[Vue warn]: Missing required prop: "fullName"

How to set a default value for a prop

Another option we can specify on an individual prop is the default option. Vue will use the default option when the attribute is not present on the tag.

Like with required , the prop has to be converted to an object to accept it as an option.

Syntax: default
props: {
  prop_name: {
    type: prop_type,
    required: true/false,
    default: default_value
  }
}

To demonstrate, let’s add a default value to our child component’s prop.

Example: src/components/GreetingMessage.vue
<template>
  <p>Hello, {{ fullName }}!</p>
</template>

<script>
export default {
  props: {
    fullName: {
      type: String,
      default: 'Unknown'
    }
  }
}
</script>

In the parent component we don’t have a prop on the tag at all so the default value should kick in.

Example: src/App.vue
<template>
  <greeting-message />
</template>

<script>
import GreetingMessage from './components/GreetingMessage'

export default {
  components: { GreetingMessage }
}
</script>

If we save and take a look at the browser, it will display the default value “Unknown”.

Output:
Hello, Unknown!

The default option only works if an attribute isn’t present on the tag. If the attribute is present but empty, Vue will show it as nothing.

To demonstrate, let’s add the prop back to the tag in the parent component but keep its value empty.

Example: src/App.vue
<template>
  <greeting-message full-name="" />
</template>

<script>
import GreetingMessage from './components/GreetingMessage'

export default {
  components: { GreetingMessage }
}
</script>

If we save and take a look at the browser, nothing is displayed where the name should be.

Output:
Hello, !

Non-prop attributes

We can add standard HTML attributes that are not props to a component.

To demonstrate, lets add a CSS class to our example.

Example: src/App.vue
<template>
  <greeting-message class="dark-mode" full-name="John Doe" />
</template>

<script>
import GreetingMessage from './components/GreetingMessage'

export default {
  components: { GreetingMessage }
}
</script>

<style scoped>
.dark-mode { margin:0;padding:1rem;background:#232323;color:#D1D1D1 }
</style>

If we save and take a look at the browser, the new styling is applied.