Back to Blog 3 min read

Optimizing Vue Performance with Prop Stability

In Vue.js, the component will re-render whenever a child component’s props change. This simple concept leads us to a crucial best practice that can s...

In Vue.js, the component will re-render whenever a child component’s props change. This simple concept leads us to a crucial best practice that can significantly enhance our app’s performance: prop stability.

prop stability in vue application

What is Prop Stability?

Prop stability means keeping the props passed to a component stable, so they don't change frequently. This helps to avoid unnecessary re-renders and enhances your app's performance.

Example: List Item Component

Imagine we have a ListItem component that receives id, message, and activeId as props. Here’s how it might look in the template:

<template>
  <div>
    <span v-if="id === activeId">➤</span> {{ message }}
  </div>
</template>

To help us see when the component updates, we can import onUpdated from Vue and log a message whenever the component re-renders:

import { onUpdated } from 'vue';

export default {
  props: {
    id: Number,
    message: String,
    activeId: Number
  },
  setup(props) {
    onUpdated(() => {
      console.log(`Updating ${props.id}`);
    });
  }
}

Setting Up the Parent Component

In the parent component, we create an array of messages, each with a unique id and message, and a ref to keep track of the active message:

import { ref } from 'vue';
import ListItem from './ListItem.vue';

export default {
  components: { ListItem },
  setup() {
    const messages = [
      { id: 0, message: 'Message 1' },
      { id: 1, message: 'Message 2' },
      { id: 2, message: 'Message 3' },
    ];
    const activeId = ref(0);

    const next = () => {
      activeId.value = (activeId.value + 1) % messages.length;
    };

    return { messages, activeId, next };
  }
}

In the template, we render the ListItem components and add a button to change the activeId:

<template>
  <ul>
    <ListItem
      v-for="item in messages"
      :key="item.id"
      :id="item.id"
      :message="item.message"
      :activeId="activeId"
    />
  </ul>
  <button @click="next">Next</button>
</template>

Identifying the Problem

When you click the button, you’ll notice that all ListItem components re-render. This happens because the activeId prop changes for every item, causing unnecessary re-renders.

Improving with Stable Props

To optimize this, we can change the ListItem component to use an isActive prop instead of activeId:

<template>
  <div>
    <span v-if="isActive">➤</span> {{ message }}
  </div>
</template>

In the parent component, we determine isActive by checking if the id matches activeId:

<template>
  <ul>
    <ListItem
      v-for="item in messages"
      :key="item.id"
      :id="item.id"
      :message="item.message"
      :isActive="item.id === activeId"
    />
  </ul>
  <button @click="next">Next</button>
</template>

With this change, only the active item and the previously active item re-render when activeId changes. This reduces the number of re-renders, improving performance, especially for large lists.

I hope this will help, Happy Coding!