What Causes This Warning?
This warning occurs when a VNode is created with NaN (Not a Number) as its key. Keys must be strings or numbers, and NaN is not a valid key because NaN !== NaN.
The Problem
<template>
<!-- ❌ Key evaluates to NaN -->
<div v-for="item in items" :key="parseInt(item.id)">
{{ item.name }}
</div>
</template>
<script setup>
const items = [
{ id: 'abc', name: 'Item 1' }, // parseInt('abc') = NaN
{ id: '123', name: 'Item 2' }
]
</script>
import { h } from 'vue'
// ❌ Key is NaN
h('div', { key: parseInt('invalid') }, 'Content')
The Fix
Ensure Valid Key Values
<template>
<!-- ✅ Use string key directly -->
<div v-for="item in items" :key="item.id">
{{ item.name }}
</div>
</template>
Validate Before Parsing
<script setup>
import { computed } from 'vue'
const items = ref([
{ id: 'abc', name: 'Item 1' },
{ id: '123', name: 'Item 2' }
])
// ✅ Validate and provide fallback
const processedItems = computed(() =>
items.value.map((item, index) => ({
...item,
safeKey: isNaN(parseInt(item.id)) ? `item-${index}` : parseInt(item.id)
}))
)
</script>
<template>
<div v-for="item in processedItems" :key="item.safeKey">
{{ item.name }}
</div>
</template>
Common Scenarios
Parsing User IDs
<script setup>
// ❌ API returns string IDs that might not be numeric
const users = ref([
{ id: 'user_1', name: 'Alice' },
{ id: 'user_2', name: 'Bob' }
])
</script>
<template>
<!-- ❌ parseInt on non-numeric string = NaN -->
<li v-for="user in users" :key="parseInt(user.id)">
{{ user.name }}
</li>
<!-- ✅ Use string ID directly -->
<li v-for="user in users" :key="user.id">
{{ user.name }}
</li>
</template>
Mathematical Operations
<script setup>
const items = ref([
{ value: 10 },
{ value: 'invalid' }, // Not a number
{ value: 20 }
])
</script>
<template>
<!-- ❌ Division might produce NaN -->
<div v-for="(item, index) in items" :key="item.value / 2">
{{ item.value }}
</div>
<!-- ✅ Use index or validate -->
<div v-for="(item, index) in items" :key="index">
{{ item.value }}
</div>
</template>
Computed Keys
<script setup>
import { computed } from 'vue'
const items = ref([{ id: null }, { id: 2 }])
// ❌ Might produce NaN
const getKey = (item) => item.id * 1
// ✅ Safe key generation
const getKey = (item, index) => {
if (item.id != null && !isNaN(item.id)) {
return item.id
}
return `fallback-${index}`
}
</script>
<template>
<div v-for="(item, index) in items" :key="getKey(item, index)">
Content
</div>
</template>
Render Functions
import { h } from 'vue'
export default {
props: ['items'],
setup(props) {
return () => {
return h('ul', props.items.map((item, index) => {
// ❌ Key might be NaN
// const key = Number(item.id)
// ✅ Validate key
const key = !isNaN(Number(item.id))
? Number(item.id)
: `item-${index}`
return h('li', { key }, item.name)
}))
}
}
}
Date-Based Keys
<script setup>
const events = ref([
{ date: '2024-01-15', title: 'Event 1' },
{ date: 'invalid', title: 'Event 2' } // Invalid date
])
</script>
<template>
<!-- ❌ Invalid date produces NaN timestamp -->
<div v-for="event in events" :key="new Date(event.date).getTime()">
{{ event.title }}
</div>
<!-- ✅ Use date string or validate -->
<div v-for="event in events" :key="event.date">
{{ event.title }}
</div>
</template>
Helper Function
// utils/safeKey.js
export function safeKey(value, fallback) {
if (value === null || value === undefined) {
return fallback
}
if (typeof value === 'number') {
return isNaN(value) ? fallback : value
}
if (typeof value === 'string') {
return value || fallback
}
return fallback
}
// Usage
import { safeKey } from './utils/safeKey'
<div v-for="(item, i) in items" :key="safeKey(item.id, `item-${i}`)">
Quick Checklist
- Don’t use
parseInt()on non-numeric strings as keys - Validate numbers before using as keys
- Use string IDs directly when available
- Check for
NaNwithisNaN()before using - Provide fallback keys (index or generated string)
- Date timestamps need validation