There are times where you'll have to force Vue to re-render a component.

Although these edge cases where Vue doesn't automatically update are rare, it's useful to know how to fix this issue when it comes up.

Most of the time this issue is just a problem with how you're using reactivity in Vue. So first, make sure that you're using Vue properly. Reactivity can be confusing, and I still get tripped up by it now and then.

Once you've tried everything else, it's time to bring out the big guns and try something different.

In this article we'll go over:

  • How to do key changing
  • How it works with multiple children
  • How we can force children to update individually
  • Why this is a last resort technique

Key-changing to force re-renders of a component

My favourite method is to use 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 changes, Vue knows that it should get rid of the old component and create a new one.

Here is a very basic way of doing it:

<template>
  <ComponentToReRender
    :key="componentKey"
  />
</template>

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

Every time forceRerender is called, our prop 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, ComponentToReRender, that will re-initialize itself and "reset" it's state.

Forcing multiple children to update

We can also apply this technique to multiple children by having multiple keys:

<template>
  <div>
    <Child
      :key="key1"
    />
    <Child
      :key="key2"
    />
  </div>
</template>

<script>
  export default {
    data() {
      return {
        key1: 0,
        key2: 0,
      };
    },
    methods: {
      forceRerender(child) {
        if (child === 1) {
          this.key1 += 1;
        } else if( child === 2) {
          this.key2 += 1;
        }
      }
    }
  }
</script>

Here we'll want to have each key separate, and update them separately as well. This allows us to force one child to update without updating the other one.

But if you want them to always update together you can reuse the same key across all of the children. However, keys are required to be unique, so this doesn't work:

<template>
  <div>
    <Child
      :key="componentKey"
    />
    <Child
      :key="componentKey"
    />
  </div>
</template>

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

Here, only the first Child component will be rendered. The second one is ignored because it has a duplicate key.

To get around this we can construct a new key for each child based on the shared key:

<template>
  <div>
    <Child
      :key="`${componentKey}-1`"
    />
    <Child
      :key="`${componentKey}-2`"
    />
  </div>
</template>

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

Because we're appending -1 and -2 to the componentKey, the two keys will always be unique, and now both components will be rendered.

If you were rendering them in a list it would look something like this:

<template>
  <div>
    <Child
      v-for="(item, index) in list"
      :key="`${componentKey}-${index}`"
    />
  </div>
</template>

<script>
  export default {
    data() {
      return {
        list: [
          // ...
        ],
        componentKey: 0,
      };
    },
    methods: {
      forceRerender(child) {
        this.componentKey += 1;
      }
    }
  }
</script>

Here we are constructing our key as ${componentKey}-${index}, so each item in the list will get a unique key. All of the components in this list will update at the same time.

Depending on what you're doing, however, it may be simpler to just put the key that you're updating on the wrapping div element:

<template>
  <div :key="componentKey">
    <Child
      v-for="item in list"
      :key="item.id"
    />
  </div>
</template>

<script>
  export default {
    data() {
      return {
        list: [
          // ...
        ],
        componentKey: 0,
      };
    },
    methods: {
      forceRerender(child) {
        this.componentKey += 1;
      }
    }
  }
</script>

There are a lot of different ways this technique can be used, and it's useful to get yourself out of a tough spot.

Please remember though, this should be one of the last things you reach for, once you've already tried everything else. Ideally your components can take advantage of Vue's robust reactivity system, and not resort to hacks like this.

But, sometimes you just need to get things done.