Vize

Vue Rules: Template Structure

Loops, conditional branches, template scope, and template containers. See all Vue rules for the complete reference and Vue Rule Options for configurable settings.

vue/require-v-for-key

Requires every v-for node to have a stable key.

Default severity: error
Presets: essential, happy-path, nuxt, opinionated

Bad:

<template>
  <li v-for="item in items">{{ item.name }}</li>
</template>

Good:

<template>
  <li v-for="item in items" :key="item.id">{{ item.name }}</li>
</template>

vue/no-use-v-if-with-v-for

Reports a node that has v-if and v-for at the same time. Filtering in a computed value keeps the list identity stable and makes the template easier to analyze.

Default severity: warning
Presets: essential, happy-path, nuxt, opinionated

Bad:

<template>
  <li v-for="item in items" v-if="item.visible" :key="item.id">
    {{ item.name }}
  </li>
</template>

Good:

<script setup lang="ts">
const visibleItems = computed(() => items.filter((item) => item.visible));
</script>

<template>
  <li v-for="item in visibleItems" :key="item.id">
    {{ item.name }}
  </li>
</template>

vue/no-child-content

Reports child content on elements that also use v-html or v-text. Vue replaces the children at runtime, so the authored content is misleading.

Default severity: error
Presets: essential, happy-path, nuxt, opinionated

Bad:

<template>
  <p v-text="message">Fallback text</p>
</template>

Good:

<template>
  <p v-text="message" />
</template>

vue/no-dupe-v-else-if

Reports repeated conditions in a v-if / v-else-if chain.

Default severity: error
Presets: essential, happy-path, nuxt, opinionated

Bad:

<template>
  <p v-if="status === 'ready'">Ready</p>
  <p v-else-if="status === 'ready'">Still ready</p>
</template>

Good:

<template>
  <p v-if="status === 'ready'">Ready</p>
  <p v-else-if="status === 'loading'">Loading</p>
</template>

vue/no-template-shadow

Reports template variables that shadow variables from an outer scope. This prevents accidental references to a different value than the reader expects.

Default severity: warning
Presets: nuxt, opinionated

Bad:

<script setup lang="ts">
const item = ref("selected");
</script>

<template>
  <p v-for="item in items" :key="item.id">{{ item.name }}</p>
</template>

Good:

<script setup lang="ts">
const selectedItem = ref("selected");
</script>

<template>
  <p v-for="item in items" :key="item.id">{{ item.name }}</p>
</template>

vue/no-lone-template

Reports a <template> element that has no structural directive. v-if, v-else-if, v-else, v-for, and v-slot justify the wrapper. The SFC's own root <template> block is not this element.

Default severity: warning
Presets: happy-path, nuxt, opinionated

Bad:

<template>
  <div>
    <template>
      <p>{{ message }}</p>
    </template>
  </div>
</template>

Good:

<template>
  <template v-if="ready">
    <p>{{ message }}</p>
  </template>
  <template v-for="item in items" :key="item.id">
    <p>{{ item.name }}</p>
  </template>
  <BaseCard>
    <template #header>
      <h2>{{ title }}</h2>
    </template>
  </BaseCard>
</template>

vue/no-template-key

Reports key on a <template> element that is not a v-for. A v-for template may carry the key. Put key on a real element otherwise.

Default severity: error
Presets: essential, happy-path, nuxt, opinionated

Bad:

<template>
  <template :key="section">
    <div>{{ item }}</div>
  </template>
</template>

Good:

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

vue/no-unused-vars

Reports a variable introduced by v-for or v-slot that the template never reads. A name that starts with _ is treated as intentionally unused.

Default severity: warning
Presets: essential, happy-path, nuxt, opinionated

Bad:

<template>
  <li v-for="(item, index) in items" :key="item.id">{{ item.name }}</li>
  <template v-slot="{ foo }">
    <span>Hello</span>
  </template>
</template>

Good:

<template>
  <li v-for="(item, index) in items" :key="index">{{ item.name }}</li>
  <li v-for="(item, _index) in items" :key="item.id">{{ item.name }}</li>
  <template v-slot="{ data }">
    <span>{{ data }}</span>
  </template>
</template>

vue/no-useless-template-attributes

Reports an attribute on <template> that Vue ignores. A <template> element only keeps structural directives (v-if, v-else-if, v-else, v-for, v-slot) and the key that belongs to a v-for.

Default severity: error
Presets: essential, happy-path, nuxt, opinionated

Bad:

<template>
  <BaseCard>
    <template v-if="show" class="panel"><div /></template>
    <template v-for="item in items" id="list"><div /></template>
    <template #header ref="header"><div /></template>
  </BaseCard>
</template>

Good:

<template>
  <BaseCard>
    <template v-if="show"><div /></template>
    <template v-for="item in items" :key="item.id"><div /></template>
    <template #header><div /></template>
  </BaseCard>
</template>