Vize

Vue Rules: Template Safety

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-duplicate-attributes Bad · Good Disallow duplicate attributes on the same element
vue/no-textarea-mustache Bad · Good Disallow mustache interpolation in <textarea>
vue/no-unsafe-url Bad · Good Warn about potentially unsafe URL bindings
vue/no-v-html Bad · Good Warn against v-html to prevent XSS vulnerabilities
vue/no-v-text-v-html-on-component Bad · Good Disallow v-text / v-html on component elements
vue/permitted-contents Bad · Good Enforce HTML content model rules
vue/use-unique-element-ids Bad · Good Enforce unique element IDs using useId() instead of static literals

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

vue/no-duplicate-attributes

Disallow duplicate attributes on the same element

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-duplicate-attributes": "error"
      }
    },
  },
});
vp run lint

Bad

The same button declares class twice instead of one combined class value.

<template>
  <button class="primary" class="large">Save</button>
</template>

Good

Both class tokens appear in a single class attribute.

<template>
  <button class="primary large">Save</button>
</template>

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

Implementation · All rules

vue/no-textarea-mustache

Disallow mustache interpolation in <textarea>

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-textarea-mustache": "error"
      }
    },
  },
});
vp run lint

Bad

The textarea places message in child interpolation instead of binding its value.

<template>
  <textarea>{{ message }}</textarea>
</template>

Good

v-model binds the editable textarea value to message.

<template>
  <textarea v-model="message"></textarea>
</template>

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

Implementation · All rules

vue/no-unsafe-url

Warn about potentially unsafe URL bindings

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-unsafe-url": "warn"
      }
    },
  },
});
vp run lint

Bad

The anchor destination begins with the executable javascript: scheme.

<template>
<a href="javascript:alert(1)">Continue</a>
</template>

Good

The anchor uses the ordinary local /next navigation destination.

<template>
<a href="/next">Continue</a>
</template>

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

Implementation · All rules

vue/no-v-html

Warn against v-html to prevent XSS vulnerabilities

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-v-html": "warn"
      }
    },
  },
});
vp run lint

Bad

v-html interprets content as HTML rather than ordinary text.

<template>
  <article v-html="content" />
</template>

Good

Mustache interpolation displays content as escaped text instead of injecting HTML.

<template>
  <article>{{ content }}</article>
</template>

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

Implementation · All rules

vue/no-v-text-v-html-on-component

Disallow v-text / v-html on component 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-v-text-v-html-on-component": "error"
      }
    },
  },
});
vp run lint

Bad

The component tag receives v-html or v-text, which replaces element content rather than supplying component slots.

<template>
  <MyComponent v-html="content" />
  <MyComponent v-text="content" />
</template>

Good

Native HTML targets can receive the directives; MyComponent receives its content through the default slot.

<template>
  <div v-html="content"></div>
  <component is="div" v-html="content" />
  <MyComponent>{{ content }}</MyComponent>
</template>

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

Implementation · All rules

vue/permitted-contents

Enforce HTML content model rules

Bad · Good

Default severity: error
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/permitted-contents": "error"
      }
    },
  },
});
vp run lint

Bad

The examples put block content in p, omit the table body, nest interactive controls, or put a div directly inside ul.

<template>
  <p><div>block in a paragraph</div></p>
  <table><tr><td>row without tbody</td></tr></table>
  <a href="#"><button type="button">nested control</button></a>
  <ul><div>not a list item</div></ul>
</template>

Good

The examples use inline paragraph content, an explicit tbody, and li children. The custom MyItem is not treated as a known native ul child.

<template>
  <p><span>inline in a paragraph</span></p>
  <table><tbody><tr><td>cell</td></tr></tbody></table>
  <ul><li>list item</li><MyItem /></ul>
</template>

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

Implementation · All rules

vue/use-unique-element-ids

Enforce unique element IDs using useId() instead of static literals

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.

Configuration (Vite+)

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

export default defineConfig({
  lint: {
    vize: {
      "preset": "incremental",
      "rules": {
        "vue/use-unique-element-ids": "warn"
      }
    },
  },
});
vp run lint

Bad

The literal email ID is reused by every instance of this component, which can misdirect its label when several instances are rendered.

<template>
  <label for="email">Email</label>
  <input id="email" />
</template>

Good

useId() produces the instance's emailId; bind the same value to the label's for and the input's id.

<script setup>
import { useId } from "vue";

const emailId = useId();
</script>

<template>
  <label :for="emailId">Email</label>
  <input :id="emailId" />
</template>

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

Implementation · All rules