Vue.js course · Module 7: Props & Events

Props Basics

6 min read
In this lesson6

Welcome to the NOVA LAB Communication Station! Here orbital systems transmit mission parameters to Martian modules - just as parent components pass props to their children. Every signal counts, because depending on where the planets are, a message from Earth takes anywhere from about 3 to more than 20 minutes to reach Mars.

Picture a panel that shows the state of one station module: its name and temperature. The oxygen generator, the water filter and the greenhouse all look identical on it; only the data differs. If the component had that data hard-coded, you would have to copy it three times and keep three files in sync with every fix. You need one component whose parameters you supply from the outside - and that is exactly what props are for.

What you will learn

At the Communication Station you will master every communication channel between components:

  • pass data from parent to child with props, then type and validate it,
  • send return signals from child to parent with your own events,
  • build components that support v-model,
  • broadcast data deep into the component tree with provide and inject,
  • combine these channels into one coherent communication architecture.

What are Props?

Props (short for properties) are a mechanism for passing data from a parent component to a child component. It is a one-way data flow - like a radio signal from Earth to Mars. The child declares its list of parameters with the defineProps macro, which works in <script setup> without an import, and the parent supplies the values like attributes in the template:

1<!-- Parent.vue -->
2<template>
3  <MarsModule name="Oxygen Generator" :temperature="-63" />
4</template>
5
6<!-- MarsModule.vue -->
7<script setup>
8const props = defineProps(['name', 'temperature'])
9</script>
10
11<template>
12  <div>{{ name }}: {{ temperature }}°C</div>
13</template>

Notice the colon before temperature. It is the v-bind shorthand, which makes -63 reach the child as a number rather than text; the name attribute without a colon passes a plain string. In the child's template, props are available directly by name, and in the script through the object returned by defineProps, that is props.name. The parent did not change a single line of the child - it only supplied values.

Typed Props

An array of names tells you which signals the module accepts, but not what kind they are. The object syntax lets you assign a type to each name, meaning a constructor such as String, Number, Boolean, Array or Object - like a transmission protocol specification:

1<script setup>
2const props = defineProps({
3  name: String,
4  temperature: Number,
5  critical: Boolean,
6  modules: Array,
7  config: Object
8})
9</script>

A type converts nothing. If the parent passes text where you expect a number, Vue prints a warning in the console in development mode, but the value still reaches the component - types are an early warning system, not a barrier. Declare multi-word names in camelCase, for example moduleName; in the parent's template you can write them as module-name, because Vue matches both forms.

Passing an object with v-bind

Sometimes you already have the module's data collected in one object. Instead of listing every field separately, pass the whole object with v-bind without an argument - each of its properties reaches the child as a separate prop:

1<template>
2  <!-- Individually -->
3  <MarsModule :name="data.name" :status="data.status" />
4
5  <!-- All at once -->
6  <MarsModule v-bind="data" />
7</template>
8
9<script setup>
10import { reactive } from 'vue'
11
12const data = reactive({
13  name: 'Oxygen Generator',
14  status: 'operational',
15  temperature: -63
16})
17</script>

The second line also passes temperature, which the first one never mentions. Thanks to reactive, every change to the data object reaches the child immediately. Fields the child did not declare as props are not lost - they become attributes passed further down, which we will look at in the lesson on fallthrough attributes.

Boolean Props

Props of type Boolean imitate HTML attributes such as disabled, which is why they have their own rule: the presence of the attribute means true, and its absence means false:

1<template>
2  <SystemModule critical />          <!-- critical = true -->
3  <SystemModule :critical="true" />  <!-- explicit true -->
4  <SystemModule :critical="false" /> <!-- explicit false -->
5  <SystemModule />                   <!-- critical = false (default) -->
6</template>

This rule applies only to props declared as Boolean. An absent prop of any other type is undefined, not false, so the child's template should be ready for that.

One-way data flow

The most important rule of this location: data flows only downwards. The parent owns the data, and the child receives it for reading. When the parent changes a value, Vue refreshes the child on its own, but the child cannot overwrite a prop:

1<script setup>
2const props = defineProps(['title'])
3
4// WRONG - don't modify props!
5// props.title = 'New title'
6
7// Create a local copy
8import { ref } from 'vue'
9const localTitle = ref(props.title)
10</script>

An attempt to assign ends with a console warning that the target is read-only, and the value stays unchanged. A local copy in a ref works well when the prop is only a starting value. Remember what the copy does not do, though: when the parent later changes title, localTitle will not follow it. Describe values derived from a prop with computed, and when the child really wants to change the parent's data, let it send the parent a signal - that is the topic of the custom events lesson.

My advice: from your very first component, declare props with the object syntax and types. An array of names is fine for a sketch, but types will catch the parent's mistake before the crew on Mars notices it. In the next lesson you will add validation to them: required fields, default values and your own conditions. In the editor below the same module appears three times - with props passed individually, through v-bind and with the critical flag.

Remember: props are a signal from mission control - the module receives and displays it, but never overwrites it.

Code for this lesson: App.vue
1<script setup>
2import { reactive } from 'vue'
3import MarsModule from './MarsModule.vue'
4
5const moduleData = reactive({
6  name: 'Oxygen Generator Alpha',
7  status: 'operational',
8  temperature: -63
9})
10</script>
11
12<template>
13  <div class="nova-lab">
14    <h1>NOVA LAB - Props Demo</h1>
15
16    <!-- Pass props individually -->
17    <MarsModule
18      :name="moduleData.name"
19      :status="moduleData.status"
20      :temperature="moduleData.temperature"
21    />
22
23    <!-- Pass all props at once -->
24    <MarsModule v-bind="moduleData" />
25
26    <!-- Boolean prop - presence = true -->
27    <MarsModule v-bind="moduleData" critical />
28  </div>
29</template>
30
31<style scoped>
32.nova-lab {
33  background: #0a0e27;
34  color: #00ff88;
35  min-height: 100vh;
36  padding: 20px;
37  font-family: monospace;
38}
39h1 { color: #00ff88; margin-bottom: 20px; }
40</style>

Spotted a mistake in this lesson?

Check yourself

Answer the questions from this lesson. Pick an answer to see right away whether it is correct.

  1. 1. What are props in Vue.js?

  2. 2. How do you declare props in <script setup>?

These are 2 of 3 questions for this lesson. Solve the rest in the game.

Hands-on tasks in the game

  • Code editor

    Create a MarsSignal component that accepts props message and delay.

  • Click in order

    Arrange the props definition with type:

  • Code editor

    Create a SystemModule component with typed props: name (String), temperature (Number), critical (Boolean).

  • Vertical ordering

    Arrange the Props data flow from parent component to display:

Useful articles