Fix: VNode Created with Invalid Key (NaN) in Vue.js

Error message:
VNode created with invalid key (NaN). VNode type:
Rendering & VNodes 2025-01-25

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 NaN with isNaN() before using
  • Provide fallback keys (index or generated string)
  • Date timestamps need validation