Fix: v-for Range Expects an Integer Value in Vue.js

Error message:
The v-for range expect an integer value but got {source}.
Directives 2025-01-25

What Causes This Warning?

This warning occurs when you use v-for with a range (number) that isn’t a valid integer. Vue’s v-for range feature expects a whole number to determine how many times to iterate.

The Problem

<!-- ❌ Using non-integer values -->
<template>
  <div v-for="n in 5.5" :key="n">{{ n }}</div>
  <div v-for="n in NaN" :key="n">{{ n }}</div>
  <div v-for="n in '3'" :key="n">{{ n }}</div>
</template>

The Fix

Use Integer Values

<template>
  <!-- ✅ Integer literal -->
  <div v-for="n in 5" :key="n">{{ n }}</div>

  <!-- ✅ Integer from ref -->
  <div v-for="n in count" :key="n">{{ n }}</div>
</template>

<script setup>
const count = ref(5) // Integer value
</script>

Convert to Integer

<script setup>
const rawValue = ref(5.7)

// ✅ Use Math.floor, Math.ceil, or Math.round
const count = computed(() => Math.floor(rawValue.value))
</script>

<template>
  <div v-for="n in count" :key="n">{{ n }}</div>
</template>

Common Scenarios

Pagination

<script setup>
const totalItems = ref(47)
const itemsPerPage = ref(10)

// ❌ Division might not be integer
// const totalPages = totalItems.value / itemsPerPage.value // 4.7

// ✅ Round up for pagination
const totalPages = computed(() =>
  Math.ceil(totalItems.value / itemsPerPage.value) // 5
)
</script>

<template>
  <button v-for="page in totalPages" :key="page">
    {{ page }}
  </button>
</template>

Star Rating

<script setup>
const rating = ref(3.5)

// ✅ Separate full and partial stars
const fullStars = computed(() => Math.floor(rating.value))
const hasHalfStar = computed(() => rating.value % 1 >= 0.5)
</script>

<template>
  <span v-for="n in fullStars" :key="n">★</span>
  <span v-if="hasHalfStar">☆</span>
</template>

Dynamic Count from API

<script setup>
const apiResponse = ref(null)

// ✅ Ensure integer with fallback
const itemCount = computed(() => {
  const count = apiResponse.value?.count
  return Number.isInteger(count) ? count : 0
})
</script>

<template>
  <div v-for="n in itemCount" :key="n">Item {{ n }}</div>
</template>

User Input

<script setup>
const inputValue = ref('')

// ✅ Parse and validate user input
const repeatCount = computed(() => {
  const parsed = parseInt(inputValue.value, 10)
  if (isNaN(parsed) || parsed < 0) return 0
  return Math.min(parsed, 100) // Cap at 100
})
</script>

<template>
  <input v-model="inputValue" type="number" min="0" max="100" />
  <div v-for="n in repeatCount" :key="n">Repeated {{ n }}</div>
</template>

Calculated Grid

<script setup>
const gridSize = ref(4.2) // Might be non-integer

// ✅ Ensure integer for grid
const rows = computed(() => Math.round(gridSize.value))
const cols = computed(() => Math.round(gridSize.value))
</script>

<template>
  <div class="grid">
    <div v-for="row in rows" :key="row" class="row">
      <div v-for="col in cols" :key="col" class="cell">
        {{ row }}-{{ col }}
      </div>
    </div>
  </div>
</template>

Using Array Instead

If you need more control, create an array:

<script setup>
const count = ref(5.5)

// ✅ Create array with exact length
const items = computed(() =>
  Array.from({ length: Math.floor(count.value) }, (_, i) => i + 1)
)
</script>

<template>
  <div v-for="item in items" :key="item">{{ item }}</div>
</template>

Type Safety with TypeScript

const count = ref<number>(5)

// TypeScript helps catch this at compile time
const safeCount = computed((): number => {
  const value = count.value
  if (!Number.isInteger(value)) {
    console.warn('Count should be an integer')
    return Math.floor(value)
  }
  return value
})

Quick Checklist

  • Ensure v-for range values are integers
  • Use Math.floor(), Math.ceil(), or Math.round() for calculations
  • Validate user input before using in v-for
  • Handle API responses that might return floats
  • Use Number.isInteger() for validation
  • Consider using arrays instead for complex cases