Vize

Vue Rules: Template Structure

Every rule on this page includes its purpose, prerequisites, configuration, and complete Bad/Good examples. Highlighted lines show the changes; copied code keeps the complete source. Each packet states its current support limits.

Rule Examples Purpose
vue/no-child-content Bad · Good Disallow child content when using v-html or v-text
vue/no-dupe-v-else-if Bad · Good Disallow duplicate conditions in v-if / v-else-if chains
vue/no-lone-template Bad · Good Disallow unnecessary <template> elements
vue/no-template-key Bad · Good Disallow key attribute on <template>
vue/no-template-shadow Bad · Good Disallow variable names that shadow variables in outer scope
vue/no-unused-vars Bad · Good Disallow unused variable definitions in v-for and v-slot directives
vue/no-use-v-if-with-v-for Bad · Good Disallow using v-if on the same element as v-for
vue/no-useless-template-attributes Bad · Good Disallow useless attributes on <template> elements
vue/require-v-for-key Bad · Good Require v-bind:key with v-for directives

All rules · Rule Options · ESLint migration map · Project checks · Attributes across components

vue/no-child-content

Disallow child content when using v-html or v-text

Bad · Good

Default severity: error
Presets: essential, happy-path, nuxt, ecosystem, opinionated
Automatic fix: None; review the suggested change
Applies to: Vue SFC templates and blocks, with script context where the rule requires it
Options: No rule-specific options. Severity and preset selection are configurable.

Configuration (Vite+)

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  lint: {
    vize: {
      "preset": "incremental",
      "rules": {
        "vue/no-child-content": "error"
      }
    },
  },
});
vp run lint

Bad

v-text replaces the paragraph content, so the authored fallback text cannot survive that directive.

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

Good

Removing the child text leaves v-text as the single source of paragraph content.

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

Good avoids this rule's finding under the configuration above; other rules may still report diagnostics.

Implementation · All rules

vue/no-dupe-v-else-if

Disallow duplicate conditions in v-if / v-else-if chains

Bad · Good

Default severity: error
Presets: essential, happy-path, nuxt, ecosystem, opinionated
Automatic fix: None; review the suggested change
Applies to: Vue SFC templates and blocks, with script context where the rule requires it
Options: No rule-specific options. Severity and preset selection are configurable.

Configuration (Vite+)

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  lint: {
    vize: {
      "preset": "incremental",
      "rules": {
        "vue/no-dupe-v-else-if": "error"
      }
    },
  },
});
vp run lint

Bad

The else-if repeats the ready condition already tested by the first branch, making that later branch unreachable.

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

Good

The second branch tests loading, a distinct state that can reach the else-if.

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

Good avoids this rule's finding under the configuration above; other rules may still report diagnostics.

Implementation · All rules

vue/no-lone-template

Disallow unnecessary <template> elements

Bad · Good

Default severity: warning
Presets: happy-path, nuxt, ecosystem, opinionated
Automatic fix: None; review the suggested change
Applies to: Vue SFC templates and blocks, with script context where the rule requires it
Options: No rule-specific options. Severity and preset selection are configurable.

Configuration (Vite+)

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  lint: {
    vize: {
      "preset": "incremental",
      "rules": {
        "vue/no-lone-template": "warn"
      }
    },
  },
});
vp run lint

Bad

The inner template has no directive or slot role that gives it a structural purpose.

<template>
<div><template><p>Details</p></template></div>
</template>

Good

Removing the unnecessary wrapper leaves the paragraph directly inside div.

<template>
<div><p>Details</p></div>
</template>

Good avoids this rule's finding under the configuration above; other rules may still report diagnostics.

Implementation · All rules

vue/no-template-key

Disallow key attribute on <template>

Bad · Good

Default severity: error
Presets: essential, happy-path, nuxt, ecosystem, opinionated
Automatic fix: None; review the suggested change
Applies to: Vue SFC templates and blocks, with script context where the rule requires it
Options: No rule-specific options. Severity and preset selection are configurable.

Configuration (Vite+)

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  lint: {
    vize: {
      "preset": "incremental",
      "rules": {
        "vue/no-template-key": "error"
      }
    },
  },
});
vp run lint

Bad

A non-loop template wrapper has a key even though it is not the keyed iteration boundary.

<template>
<template :key="section"><div>Details</div></template>
</template>

Good

The key belongs to a template v-for iteration, where it identifies each repeated fragment.

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

Good avoids this rule's finding under the configuration above; other rules may still report diagnostics.

Implementation · All rules

vue/no-template-shadow

Disallow variable names that shadow variables in outer scope

Bad · Good

Default severity: warning
Presets: nuxt, opinionated
Automatic fix: None; review the suggested change
Applies to: Vue SFC templates and blocks, with script context where the rule requires it
Options: No rule-specific options. Severity and preset selection are configurable.

The current check compares nested v-for bindings. It does not report a single v-for binding merely because it shares a script binding's name.

Configuration (Vite+)

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  lint: {
    vize: {
      "preset": "incremental",
      "rules": {
        "vue/no-template-shadow": "warn"
      }
    },
  },
});
vp run lint

Bad

The inner v-for declares item again and hides the outer item binding inside the nested loop.

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

Good

The inner loop declares child, leaving item available for the outer row and child for the nested row.

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

Good avoids this rule's finding under the configuration above; other rules may still report diagnostics.

Implementation · All rules

vue/no-unused-vars

Disallow unused variable definitions in v-for and v-slot directives

Bad · Good

Default severity: warning
Presets: essential, happy-path, nuxt, ecosystem, opinionated
Automatic fix: None; review the suggested change
Applies to: Vue SFC templates and blocks, with script context where the rule requires it
Options: No rule-specific options. Severity and preset selection are configurable.

Configuration (Vite+)

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  lint: {
    vize: {
      "preset": "incremental",
      "rules": {
        "vue/no-unused-vars": "warn"
      }
    },
  },
});
vp run lint

Bad

The loop declares an unused index and the slot declares foo without referencing it.

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

Good

The examples consume index or mark it intentionally unused as _index, and the slot renders data. Index keys are only a usage example here, not a recommendation for stable item identity.

<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>

Good avoids this rule's finding under the configuration above; other rules may still report diagnostics.

Implementation · All rules

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

Disallow using v-if on the same element as v-for

Bad · Good

Default severity: warning
Presets: essential, happy-path, nuxt, ecosystem, opinionated
Automatic fix: None; review the suggested change
Applies to: Vue SFC templates and blocks, with script context where the rule requires it
Options: No rule-specific options. Severity and preset selection are configurable.

Configuration (Vite+)

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  lint: {
    vize: {
      "preset": "incremental",
      "rules": {
        "vue/no-use-v-if-with-v-for": "warn"
      }
    },
  },
});
vp run lint

Bad

The same list element combines v-if and v-for and tests visibility through the loop binding.

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

Good

A computed collection filters the visible items before the template iterates over them.

<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>

Good avoids this rule's finding under the configuration above; other rules may still report diagnostics.

Implementation · All rules

vue/no-useless-template-attributes

Disallow useless attributes on <template> elements

Bad · Good

Default severity: error
Presets: essential, happy-path, nuxt, ecosystem, opinionated
Automatic fix: None; review the suggested change
Applies to: Vue SFC templates and blocks, with script context where the rule requires it
Options: No rule-specific options. Severity and preset selection are configurable.

Configuration (Vite+)

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  lint: {
    vize: {
      "preset": "incremental",
      "rules": {
        "vue/no-useless-template-attributes": "error"
      }
    },
  },
});
vp run lint

Bad

The conditional template has a class, but this structural wrapper does not render a DOM element to receive it.

<template>
<section><template v-if="ready" class="notice"><p>Ready</p></template></section>
</template>

Good

The class moves to the paragraph that actually renders while v-if stays on the structural template.

<template>
<section><template v-if="ready"><p class="notice">Ready</p></template></section>
</template>

Good avoids this rule's finding under the configuration above; other rules may still report diagnostics.

Implementation · All rules

vue/require-v-for-key

Require v-bind:key with v-for directives

Bad · Good

Default severity: error
Presets: essential, happy-path, nuxt, ecosystem, opinionated
Automatic fix: None; review the suggested change
Applies to: Vue SFC templates and blocks, with script context where the rule requires it
Options: No rule-specific options. Severity and preset selection are configurable.

Configuration (Vite+)

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  lint: {
    vize: {
      "preset": "incremental",
      "rules": {
        "vue/require-v-for-key": "error"
      }
    },
  },
});
vp run lint

Bad

Each repeated <li> lacks a key that identifies its corresponding item during list updates.

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

Good

:key="item.id" gives each repeated node the item's identity rather than its current position.

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

Good avoids this rule's finding under the configuration above; other rules may still report diagnostics.

Implementation · All rules