Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

vincent15000's avatar

v-for and key : is it a good idea to use md5 to generate a key ?

Hello,

To display an array of data on the screen and see dynamic changes (the array is refreshed when I receive an event from the websocket), I need to customize the key according to what could change.

This doesn't work because the id doesn't change, but only some internal nested properties inside the item object.

<div v-for="(item, index) in items" :key="'model-'+item.id>
	// some data to display
</div>

So I have decided to write other keys.

<div v-for="(item, index) in items" :key="'model-'+item.id+'-notification-'+item.notification.count+'-parent-'+item.parent.id+...">
	// some data to display
</div>

But this becomes quickly difficult to maintain.

So I have searched and found another way.

<div v-for="(item, index) in items" :key="md5(item)">
	// some data to display
</div>

What do you think about using md5 to generate a key ? Isn't it too heavy / slow when I will have a lot of items inside the array ?

I have read that in some rare cases, it's possible to have the same md5 checksum for 2 different set of datas.

For the moment md5 seems to work perfect for me, but if you have any other idea, what would you suggest me ?

Thanks a lot for your answer.

V

0 likes
16 replies
vincent15000's avatar

AI seems to recommend not using md5, but another way : stringify the item, good idea ... but why not just JSON.stringify(item) ?

I just have another idea ... what about generating a unique identifier in the backend and save one for each row in each table ? Each time the model changes, I get a new unique identifier.

But how could I generate this unique identifier given that with md5 there can be hash collisions ?

martinbean's avatar

@vincent15000 You’re completely over-complicating things. If items have an id property, then just use that as the key:

<div v-bind:key="item.id" v-for="item in items">
    <!-- // -->
</div>

So long as each item is reactive, then the item template will be re-rendered if something inside it changes.

1 like
vincent15000's avatar

@martinbean That's what I did, but it didn't work when I tried.

But effectively you're right, now it works ... that means that it didn't work for me but for another reason.

vincent15000's avatar

@martinbean Are the components isolated ?

For example if I have on the same page 2 different components : BooksComponent and CategoriesComponent : both display books (that's just to give an example).

So in both components, I will have a loop to display books and I will perhaps have the same :key="'book-'+book.id".

That means that on the same page, I will have two components with the same key.

Is it a problem ?

martinbean's avatar
Level 80

@vincent15000 The key should identify a record in that specific loop. This is fine:

<ul>
  <li v-bind:key="book.id" v-for="book in books">
    <span>{{ book.name }}</span>
  </li>
</ul>

<ul>
  <li v-bind:key="category.id" v-for="category in categories">
    <span>{{ category.name }}</span>
  </li>
</ul>

Updating say, a single book will only cause the books list to be re-rendered:

const books = ref([
  {
    id: 1,
    name: 'War and Peace',
  },
  {
    id: 2,
    name: 'Nineteen Eighty Four',
  },
]);

// Will cause only books list to re-rendered
books.value[1].name = '1984';
1 like
vincent15000's avatar

@martinbean Ok so it's not a problem if I have this ?

// component 1
<ul>
  <li v-bind:key="book.id" v-for="book in books">
    <span>{{ book.name }}</span>
  </li>
</ul>

// component 2
<ul>
  <li v-bind:key="category.id" v-for="category in categories">
    <span>{{ category.name }}</span>
	<ul>
		<li v-bind:key="book.id" v-for="book in category.books">
			<span>{{ book.name }}</span>
		</li>
	</ul>
  </li>
</ul>
vincent15000's avatar

@martinbean I have to searched again, because the problem is not solved.

With a simple "'book-'+book.id" key, it doesn't work.

I wanted to simplify for the post with books, but the real datas are here.

endpoint.active_connection.state.color

If I write a simple key, it doesn't work.

<component :is="EndpointComponent" :item="endpoint" v-for="(endpoint, index) in endpoints" :key="endpoint.id">
</component>

When the color changes, it is not updated on the screen.

The component is like this.

// Template of the YHexagon component
<y-hexagon
    :borderColor1="endpoint.active_connection.state.color"
>
	<slot></slot>
</y-hexagon>

And here is the template of the YHexagon component.

<div
    class="transition-all hexagon-border-1 absolute"
    :style="hexagonContentBorderStyle1"
></div>
const hexagonContentBorderStyle1 = computed(() => {
    return {
        width: `${calculatedContentBorderBlockWidth.value}px`,
        height: `${calculatedContentBorderBlockHeight.value}px`,
        top: `${calculatedBorderWidth.value}px`,
        left: `${calculatedBorderWidth.value}px`,
        backgroundColor: `${props.borderColor1}`,
    };
});

The calculared values are computed properties.

Is there anything that prevents for a good reactivity ?

Thanks for your help.

vincent15000's avatar

Any idea why it doesn't work with a simple key ?

richardhulbert's avatar

First off have I would look at the object using Vue tools in particular the computed property.

1 Does it change when properties change? 2 Is it outputting what you think it should?

Are you missing a ; at the end of each line?

If you are using Vue 3 you don't really need a key in any event, why not use index?

<component :is="EndpointComponent" :item="endpoint" v-for="(endpoint, index) in endpoints" :key="index">
</component>

R

1 like
vincent15000's avatar

@richardhulbert

Just tested, but the new datas are not updated on the screen.

And yes I have checked if the array is really updated and it's the case.

richardhulbert's avatar

So you are using the Vue tool to look at the component and specifically the computed property (rather than looking at the rendered result in the page)?

1 like
vincent15000's avatar

@martinbean @richardhulbert

I have a loop to display all endpoints.

Each time any (deep) property of an endpoint object is changed, I splice the endpoint object in the dashboardStore.endpoints array which is a ref in the pinia store.

In the component in which I display the endpoints, I filter the endpoints to keep only those having to be displayed.

const endpoints = computed(() => {
    console.log(dashboardStore.endpoints.filter(endpoint => endpoint.active_connection.branch_id === (props.branch ? props.branch.id : null)));
    return dashboardStore.endpoints.filter(endpoint => endpoint.active_connection.branch_id === (props.branch ? props.branch.id : null));
});

The console.log() shows that the endpoints computed property is updated, but the DOM isn't refreshed.

As I have to merge different types of objects, I have this computed property.

const arrayOfItems = computed(() => {
    const items = [];

    endpoints.value.forEach(endpoint => {
        items.push({
            component: EndpointComponent,
            type: 'endpoint',
            object: endpoint,
        });
    });

    networks.value.forEach(network => {
        items.push({
            component: NetworkComponent,
            type: 'network',
            object: network,
        });
    });

    return items;
});
<div v-if="arrayOfItems.length > 0" class="devices-container self-center md:self-start flex flex-col gap-2">
    <div
        v-for="(row, rowIndex) in itemsIndexes"
        :key="'items-row-'+rowIndex"
        class="flex gap-2"
        :class="{ 'ml-[104px]': rowIndex % 2 !== 0, '-mt-[57.7350px]': rowIndex > 0 }"
    >
        <component
            v-for="(item, itemIndex) in arrayOfItems.filter(function (subItem, subItemIndex) {
                return subItemIndex >= row.startIndex && subItemIndex < row.endIndex;
            })"
            :key="'item-'+item.type+'-'+item.object.id"
            :is="item.component"
            :item="item.object"
            :data-id="item.object.id"
        ></component>
    </div>
</div>

Do you have any idea why it doesn't refresh the DOM ?

Please or to participate in this conversation.