Work a value out from other data with computed, so it is worked out again whenever that data changes, and see why a plain calculation goes stale.
A basket total, a score out of ten, the number of unread messages: each is worked out from other data, and must never go stale.
Some values are not data of their own: they are worked out from other data. A ticket total comes from the numbers of adults and children. If the numbers change, the total must be worked out again.
const total = computed(() => adults.value * 12 + children.value * 5);
computed takes a little function that works the value out. Vue runs it again whenever adults or children changes, so {{ total }} is always right. () => … is a short way to write a function that gives back what follows the arrow.
The hard part: a plain calculation, const total = adults.value * 12 + …, is worked out once, when the page starts, and then never again.
Make your prediction first. Help opens once you've locked it in.
<div id="app">
<p>Adults ($12): {{ adults }} <button @click="adults++">+</button></p>
<p>Children ($5): {{ children }}</p>
<p>Total: ${{ total }}</p>
</div>
<script src="vue.js"></script>
<script>
const { createApp, ref, computed } = Vue;
createApp({
setup() {
const adults = ref(2);
const children = ref(1);
const total = computed(() => adults.value * 12 + children.value * 5);
return { adults, children, total };
}
}).mount("#app");
</script>
The page starts at 2 adults and 1 child: $29.
Press Run and change the numbers. Watch the total, and the message that appears at $50.
<div id="app">
<h1>Concert tickets</h1>
<p>Adults ($12): {{ adults }}
<button @click="adults++">+</button> <button @click="adults--">−</button></p>
<p>Children ($5): {{ children }}
<button @click="children++">+</button> <button @click="children--">−</button></p>
<p><b>Total: ${{ total }}</b></p>
<p v-if="total >= 50">A free programme comes with your tickets!</p>
</div>
<script src="vue.js"></script>
<script>
const { createApp, ref, computed } = Vue;
createApp({
setup() {
const adults = ref(2);
const children = ref(1);
const total = computed(() => adults.value * 12 + children.value * 5);
return { adults, children, total };
}
}).mount("#app");
</script>
<div id="app">
<h1>Concert tickets</h1>
<p>Adults ($12): {{ adults }}
<button @click="adults++">+</button> <button @click="adults--">−</button></p>
<p>Children ($5): {{ children }}
<button @click="children++">+</button> <button @click="children--">−</button></p>
<p><b>Total: ${{ total }}</b></p>
<p v-if="total >= 50">A free programme comes with your tickets!</p>
</div>
<script src="vue.js"></script>
<script>
const { createApp, ref, computed } = Vue;
createApp({
setup() {
const adults = ref(2);
const children = ref(1);
const total = computed(() => adults.value * 12 + children.value * 5);
return { adults, children, total };
}
}).mount("#app");
</script>
The total after each click.
Worked out, not set.
The total goes stale.
Start with 2 adults and 1 child. Fill in the page after each click.
| after | adults | children | total ($) | free programme? |
|---|---|---|---|---|
| the start | 2 | 1 | 29 | no |
| Adults + | ||||
| Children + | ||||
| Adults + |
Clicking Adults + changes the number of adults, but the total stays at $29. There is no error. Click the line that is wrong.
A second computed.
The number of people is worked out from adults and children.
const people = computed(() => adults.value + children.value); then return it and show {{ people }}.

An if inside computed.
A computed can have several lines, as long as it returns the value at the end.
Inside computed(() => { … }): let sum = the old total; if 6 or more people, take 10 away; return sum;

Show the number of people, worked out with computed.
This ticks when your page has all of the above.
Groups of 6 or more people get $10 off the total.
This ticks when your page has all of the above.
One pancake needs 25 g of flour and 40 ml of milk. Show how much of each you need for any number of pancakes, each with computed. Add eggs too if you like (one for every four pancakes: Math.ceil(pancakes.value / 4)).
Show at least two ingredients, each worked out from the number of pancakes with computed.
This ticks when your page has all of the above.
Next: components, tags of your own.
Students predict a ticket total after two clicks, trace the total and a v-if message, find a plain calculation that goes stale, add a people count and a group deal, and make a recipe scaler. About 50 minutes.
Open the teacher guide: answers, solutions, slides and a worksheet.
Read the unit primer first. computed(() => adults.value * 12 + children.value * 5) gives a value Vue works out again whenever the refs it reads change. On the page it is used like a ref ({{ total }}, v-if="total >= 50"). The arrow () => is a short function that gives back what follows it; students do not need more about arrow functions than that.
The planted bug is the idea the course names as hard: a value derived from data must be computed, or it is worked out once when setup runs and then goes stale. The page shows no error. A useful contrast: a ref is data you set; a computed is data you never set, only work out.
Prices are invented for the example.
Students make the total a ref and update it in every button. Ask "what is the total worked out from?" and "what if someone adds a button and forgets?"
Stretch: Stop the numbers going below 0 (a computed can't do that: change the buttons, or use Math.max). Add a computed message that says how far from $50 the order is.
Support: Give the people computed written out; students only return and show it.
Next lesson: components with props. Class idea: name three values on a shopping site that are computed (total, item count, delivery cost).