Transitioning a Prop Change

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

When we click the button, I want to also transition the title. Right now, it changes instantly, which is not that big of a deal... but we can do better. Back at your editor, find the <title component and wrap it in a transition. Copy the name and mode from earlier and use those here too.

<template>
<div :class="[$style.component, 'container-fluid']">
<div class="row">
... lines 4 - 18
<div class="col-xs-12 col-lg-9">
<transition
name="fade"
mode="out-in"
>
<title-component :text="pageTitle" />
</transition>
... lines 26 - 59
</div>
</div>
</div>
</template>
... lines 64 - 180

So the big question is: does this work? Are we allowed to transition a prop change? In this case, we're not hiding and showing a component, we're just changing the value passed to the text prop.

The answer is... sort of. If you try it right now - I'll close my dev tools so we can see things - it does not work. Well, the title did change, but it still changes instantly: there's no transition.

The truth is that there is no built-in way for Vue to transition a prop change. And... that makes sense! All Vue knows how to do is add some classes when an entire element or component is being hidden or shown. A prop change is much more complex than that: who knows how we might be using it!

Vue does have documentation about how you can add transitions for a prop change, but mostly, you're doing it manually on a case-by-case basis.

Transitions and the key Prop

However, it is possible to, sort of, make this work. Over on the title component, add a new key prop. Yea, that's the prop we normally use in loops. Set this to something that will be unique for each title, like currentState, because we know the page title is based on the currentState.

<template>
<div :class="[$style.component, 'container-fluid']">
<div class="row">
... lines 4 - 18
<div class="col-xs-12 col-lg-9">
<transition
name="fade"
mode="out-in"
>
<title-component
:key="currentState"
:text="pageTitle"
/>
</transition>
... lines 29 - 62
</div>
</div>
</div>
</template>
... lines 67 - 183

Move over and try it now. Ah! It works! The title fades out and fades in just like the rest of the page!

So... how and why did that work? Especially right after I explained to you that you can't transition prop changes!

When we click the button, our top-level component re-renders and the title component now has a new value for its key prop. That actually tells Vue that the new title component should be a totally different instance than the one it was using before.

In other words, instead of re-rendering the same component instance... but passing in a new text prop, Vue completely destroys the old title component and creates a brand new one. This allows Vue to transition between the old and new title because the old component is hidden and the new component is shown.

This works great in our case... but it's kind of a "nuclear" option. If our title component had some data inside of it, that data would be completely lost each time we switched... because we're destroying and recreating the component. So it might work in some cases... but not in others.

For us, I'm super happy with this. So next: let's add a form to the checkout page!

Leave a comment!

This course is also built to work with Vue 3!

What JavaScript libraries does this tutorial use?

// package.json
{
    "devDependencies": {
        "@fortawesome/fontawesome-free": "^5.15.1", // 5.15.1
        "@symfony/webpack-encore": "^0.30.0", // 0.30.2
        "axios": "^0.19.2", // 0.19.2
        "bootstrap": "^4.4.1", // 4.5.3
        "core-js": "^3.0.0", // 3.6.5
        "eslint": "^6.7.2", // 6.8.0
        "eslint-config-airbnb-base": "^14.0.0", // 14.2.0
        "eslint-plugin-import": "^2.19.1", // 2.22.1
        "eslint-plugin-vue": "^6.0.1", // 6.2.2
        "regenerator-runtime": "^0.13.2", // 0.13.7
        "sass": "^1.29.0", // 1.29.0
        "sass-loader": "^8.0.0", // 8.0.2
        "vue": "^2.6.11", // 2.6.12
        "vue-loader": "^15.9.1", // 15.9.4
        "vue-template-compiler": "^2.6.11", // 2.6.12
        "webpack-notifier": "^1.6.0" // 1.8.0
    }
}