The correct way to force Vue to re-render a component

Sometimes Vue's reactivity system isn't enough, and you just need to re-render a component.

Or maybe you just want to blow away the current DOM and start over.

So how do you get Vue to reload a component the right way?

The best way to force Vue to re-render a component is to set a :key on the component. When you need the component to be re-rendered, you just change the value of the key and Vue will re-render the component.

This is called the Key-Changing Technique, and it's a pretty simple solution, right?

What we'll cover in this article

In this article we’ll look at a few other ways to solve this problem that work in both Vue 2 and Vue 3:

  • The horrible way: reloading the entire page
  • The terrible way: using the v-if hack
  • The better way: using Vue's built-in forceUpdate method
  • The best way: using the Key-Changing Technique to refresh your component

However, if you need to force a reload or force an update, there's probably a better way.

It's likely that you're misunderstanding one of the following tricky things:

  1. Vue's reactivity
  2. Computed props
  3. Watchers
  4. Not using a :key attribute with v-for

Now, there are valid use cases for forcing an update. Most of these will be solved using the Key-Changing Technique that's at the bottom of this article.

Let’s get started, shall we?

Horrible way: reload the entire page

This is equivalent to restarting your computer every time you want to close an app.

I guess it would work some of the time, but it's a pretty bad solution.

There isn't really much more to say about this. Don't do it!

Let's look for a better way.

Terrible way: the v-if hack

A slightly better solution is to get clever with our use of the v-if directive.

In your template you'll add the v-if directive on the component you want to reload:

<template>
	<MyComponent v-if="renderComponent" />
</template>

In your script setup you'll add in the forceRerender method that uses nextTick:

import { nextTick, ref } from 'vue';
const renderComponent = ref(true);

const forceRerender = async () => {
  // Remove MyComponent from the DOM
  renderComponent.value = false;

	// Wait for the change to get flushed to the DOM
	await nextTick();

	// Add the component back in
  renderComponent.value = true;
};

If you’re using the Options API instead of the Composition API, it will look like this:

export default {
  data() {
    return {
      renderComponent: true,
    };
  },
  methods: {
    async forceRerender() {
      // Remove MyComponent from the DOM
      this.renderComponent = false;

			// Wait for the change to get flushed to the DOM
      await this.$nextTick();

      // Add the component back in
      this.renderComponent = true;
    }
  }
};

This is what's going on here:

  1. Initially renderComponent is set to true, so MyComponent is rendered
  2. When we call forceRerender we immediately set renderComponent to false
  3. We stop rendering MyComponent because the v-if directive now evaluates to false
  4. We wait a tick, and then set renderComponent back to true
  5. Now the v-if directive evaluates to true again, so we start rendering a refreshed instance of MyComponent

There are two pieces that are important in understanding how this works.

First, we have to wait until the next tick or we won't see any changes.

In Vue, a tick is a single DOM update cycle. Vue will collect all updates made in the same tick, and at the end of a tick it will update what is rendered into the DOM based on these updates.

If we don't wait until the next tick, our updates to renderComponent will just cancel themselves out, and nothing will change.

Second, Vue will create an entirely new and refreshed component when we render the second time. This is because Vue will destroy the first one and create a new one. This means that our new MyComponent will go through all of its lifecycles as normal — created, mounted, and so on.

This works, but it isn't a great solution. I call it a hack because we're hacking around what Vue wants us to do.

So instead, let's do what Vue wants us to do!

Better way: You can use forceUpdate

This is one of the two best ways to solve this problem, both of which are officially supported by Vue.

Normally, Vue will react to changes in dependencies by updating the view. However, when you call forceUpdate, you can force that update to occur, even if none of the dependencies has actually changed.

This circumvents the entire reactivity system, which is why it’s not recommended as a solution.

But sometimes, Vue's reactivity system can be confusing, and we think that Vue will react to changes to a certain property or variable, but it doesn't.

If you’re using Vue 2, there are certain cases where Vue's reactivity system won't detect any changes at all. But Vue 3 has a much more robust proxy-based reactivity system that doesn’t suffer from these same issues.

Again, if you need to force your component to re-render or reload, there's probably a better way. You’re likely just treating the symptom and not the actual underlying issue.

Here’s how you can call forceUpdate using the Options API:

export default {
  methods: {
    methodThatForcesUpdate() {
      // ...
      this.$forceUpdate();  // Notice we have to use a $ here
      // ...
    }
  }
}

In order to call it using the Composition API in Vue 3, we have to get a little clever since this method only lives on the component instance:

import { getCurrentInstance } from 'vue';

const methodThatForcesUpdate = () => {
  // ...
  const instance = getCurrentInstance();
  instance.proxy.forceUpdate();
  // ...
};

Important: This will not update any computed properties you have. Calling forceUpdate will only force the view to re-render.

The best way: The Key-Changing Technique

There are many cases where you will have a legitimate need to force refresh a component.

To do this the proper way, we will supply a key attribute so Vue knows that a specific component is tied to a specific piece of data. If the key stays the same, it won't change the component, but if the key does change, Vue knows that it should get rid of the old component and create a new one.

Exactly what we need!

First, I’ll explain the Key-Changing Technique. Then, I’ll take a moment to explain why we use the key attribute in Vue.

Key-changing to force a component refresh

Finally, here is the very best way (in my opinion) to force Vue to refresh a component.

We add a key attribute to our component, and then change that key whenever we need the component to be re-rendered.

Here is a very basic way of doing it with script setup:

<template>
  <MyComponent :key="componentKey" />
</template>
import { ref } from 'vue';
const componentKey = ref(0);

const forceRerender = () => {
  componentKey.value += 1;
};

Here’s how you’d do it with the Options AP if you’re not using Vue 3 or the Composition API:

export default {
  data() {
    return {
      componentKey: 0,
    };
  },
  methods: {
    forceRerender() {
      this.componentKey += 1;
    }
  }
}

Every time that forceRerender is called, the value of componentKey will change. When this happens, Vue will know that it has to destroy the component and create a new one.

What you get is a child component that will re-initialize itself and "reset" it's state, forcing a refresh and re-render of the component.

A simple and elegant way to solve our problem!

I write about this key-changing technique in more detail here.

Why do we need to use a key in Vue?

We can dig a little deeper into what this key attribute is actually doing here, and why we need it.

Let's say you're rendering a list of components that has one or more of the following:

  • It's own local state
  • Some sort of initialization process, either in the setup function with Vue 3 or in the created and mounted hooks if using the Options API
  • Non-reactive DOM manipulation, through jQuery or vanilla APIs

If you sort that list, or update it in any other way, you'll need to re-render parts of the list. But you won't want to re-render everything in the list, just the things that have changed.

To help Vue keep track of what has changed and what hasn't, we supply a key attribute. Using the index of an array is not helpful here, since the index is not tied to specific objects in our list.

Here is an example list that we have:

const people = [
  { name: 'Evan', age: 34 },
  { name: 'Sarah', age: 98 },
  { name: 'James', age: 45 },
];

If we render it out using indexes we will get this:

<ul>
  <li v-for="(person, index) in people" :key="index">
    {{ person.name }} - {{ index }}
  </li>
</ul>
Evan - 0
Sarah - 1
James - 2

If we remove Sarah, we will get:

Evan - 0
James - 1

The index associated with James is changed, even though James is still James. James will be re-rendered, even if we don't want him to be.

We want to use some sort of unique id, however we end up generating it:

const people = [
  { id: 'this-is-an-id', name: 'Evan', age: 34 },
  { id: 'unique-id', name: 'Sarah', age: 98 },
  { id: 'another-unique-id', name: 'James', age: 45 },
];
<ul>
  <li v-for="person in people" :key="person.id">
    {{ person.name }} - {{ person.id }}
  </li>
</ul>

Before when we removed Sarah from our list, Vue deleted the components for Sarah and James, and then created a new component for James. Now, Vue knows that it can keep the two components for Evan and James, and all it has to do is delete Sarah's.

But it gets even better.

If we add a person to the list, it also knows that it can keep all of the existing components, and it only has to create a single new component and insert it into the correct place. This is really useful, and helps us a lot when we have more complex components that have their own state, have initialization logic, or do any sort of DOM manipulation.

Just remember, if you find yourself needing to force Vue to re-render a component, there’s probably a better way.

If, however, you do need to re-render something, choose the Key-Changing Technique over anything else.

🎉 Get 30% off Vue Tips Collection!
Get it now!