Vize

Vue Rules

Vue rules are Patina single-file rules. They inspect SFC template structure, directive syntax, component naming, and Vue-specific correctness hazards before the code reaches the runtime.

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

Reports writes to props. The owning component should update the value through an event or a model binding.

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

Bad:

<script setup lang="ts">
const props = defineProps<{ count: number }>();

props.count++;
</script>

Good:

<script setup lang="ts">
const props = defineProps<{ count: number }>();
const emit = defineEmits<{ "update:count": [value: number] }>();

function increment() {
  emit("update:count", props.count + 1);
}
</script>

vue/no-v-html

Reports v-html because it renders raw HTML and can turn user-controlled content into an XSS sink.

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

Bad:

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

Good:

<template>
  <article>{{ content }}</article>
</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-duplicate-attributes

Reports duplicate attributes on the same element.

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

Bad:

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

Good:

<template>
  <button class="primary large">Save</button>
</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-unsafe-url

Reports URL bindings and static URL attributes that may resolve to unsafe schemes such as javascript:, vbscript:, or executable data: payloads.

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

Bad:

<template>
  <iframe src="javascript:alert(1)"></iframe>
  <object data="data:text/html,<script>alert(1)</script>"></object>
  <img srcset="/safe.png 1x, javascript:alert(1) 2x" />
  <a :href="nextUrl">Continue</a>
</template>

Good:

<script setup lang="ts">
const rawNextUrl = ref("/next");
const nextUrl = computed(() => {
  return rawNextUrl.value.startsWith("/") ? rawNextUrl.value : "/";
});
</script>

<template>
  <iframe src="/embedded/report" title="Report"></iframe>
  <img srcset="/avatar.png 1x, /[email protected] 2x" />
  <a :href="nextUrl">Continue</a>
</template>

vue/no-unused-components

Reports locally registered components that never appear in the template.

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

Bad:

<script setup lang="ts">
import UserAvatar from "./UserAvatar.vue";
</script>

<template>
  <p>{{ user.name }}</p>
</template>

Good:

<script setup lang="ts">
import UserAvatar from "./UserAvatar.vue";
</script>

<template>
  <UserAvatar :user="user" />
</template>

vue/no-unused-properties

Reports props declared through defineProps that are not used by the component.

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

Bad:

<script setup lang="ts">
defineProps<{ title: string; description: string }>();
</script>

<template>
  <h1>{{ title }}</h1>
</template>

Good:

<script setup lang="ts">
defineProps<{ title: string; description: string }>();
</script>

<template>
  <h1>{{ title }}</h1>
  <p>{{ description }}</p>
</template>

vue/require-component-is

Reports <component> without an is binding.

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

Bad:

<template>
  <component />
</template>

Good:

<template>
  <component :is="currentComponent" />
</template>

vue/use-unique-element-ids

Reports static literal IDs in places where useId() is safer for component reuse and SSR.

Default severity: warning Presets: nuxt, opinionated

Bad:

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

Good:

<script setup lang="ts">
const emailId = useId();
</script>

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

Syntax And Style Rules

These rules do not need long examples, but they still behave as first-class rules and can be configured by name.

vue/attribute-hyphenation enforces attribute naming style on custom components. Default: warning. Presets: happy-path, nuxt, opinionated.

vue/attribute-order enforces a stable attribute order. Default: warning. Presets: happy-path, nuxt, opinionated.

vue/component-definition-name-casing enforces PascalCase component definition names. Default: warning. Presets: happy-path, nuxt, opinionated.

vue/component-name-in-template-casing enforces component name casing in templates. Default: warning. Presets: nuxt, opinionated.

vue/html-quotes enforces quote style for HTML attributes. Default: warning. Presets: happy-path, nuxt, opinionated.

vue/html-self-closing enforces self-closing style. Default: warning. Presets: nuxt, opinionated.

vue/multi-word-component-names requires component names to contain more than one word. Default: error. Presets: essential, nuxt, opinionated.

vue/mustache-interpolation-spacing enforces spacing inside mustache interpolation. Default: warning. Presets: happy-path, nuxt, opinionated.

vue/no-boolean-attr-value disallows explicit values for boolean HTML attributes. Default: warning. Presets: nuxt, opinionated.

vue/no-inline-style discourages inline style attributes. Default: warning. Presets: nuxt, opinionated.

vue/no-lone-template disallows unnecessary <template> wrappers. Default: warning. Presets: happy-path, nuxt, opinionated.

vue/no-multi-spaces disallows repeated spaces in templates. Default: warning. Presets: happy-path, nuxt, opinionated.

vue/no-preprocessor-lang discourages CSS preprocessor languages in SFC blocks. Default: warning. Presets: nuxt, opinionated.

vue/no-reserved-component-names disallows reserved HTML or Vue names as component names. Default: error. Presets: essential, happy-path, nuxt, opinionated.

vue/no-script-non-standard-lang discourages non-standard script languages. Default: warning. Presets: nuxt, opinionated.

vue/no-src-attribute discourages external src attributes on SFC blocks. Default: warning. Presets: nuxt, opinionated.

vue/no-template-key disallows key on <template>. Default: error. Presets: essential, happy-path, nuxt, opinionated.

vue/no-template-lang discourages lang on <template>. Default: warning. Presets: nuxt, opinionated.

vue/no-textarea-mustache disallows mustache interpolation inside <textarea>. Default: error. Presets: essential, happy-path, nuxt, opinionated.

vue/no-unused-vars reports unused variables introduced by v-for and v-slot. Default: warning. Presets: essential, happy-path, nuxt, opinionated.

vue/no-useless-template-attributes disallows attributes on <template> that Vue ignores. Default: error. Presets: essential, happy-path, nuxt, opinionated.

vue/no-v-text-v-html-on-component disallows v-text or v-html on component elements. Default: error. Presets: essential, happy-path, nuxt, opinionated.

vue/permitted-contents enforces HTML content model rules inside Vue templates. Default: error. Presets: happy-path, nuxt, opinionated.

vue/prefer-props-shorthand recommends shorthand syntax for props. Default: warning. Presets: nuxt, opinionated.

vue/prop-name-casing enforces kebab-case prop names in templates. Default: warning. Presets: happy-path, nuxt, opinionated.

vue/require-component-registration requires explicit component import or registration. Default: warning. Presets: opinionated.

vue/require-scoped-style requires scoped on SFC style blocks. Default: warning. Presets: happy-path, nuxt, opinionated.

vue/scoped-event-names recommends scoped event names such as form:submit. Default: warning. Presets: nuxt, opinionated.

vue/sfc-element-order enforces the order of top-level SFC blocks. Default: warning. Presets: happy-path, nuxt, opinionated.

vue/single-style-block recommends keeping styles in a single block. Default: warning. Presets: happy-path, nuxt, opinionated.

vue/use-v-on-exact enforces .exact when modifier-based handlers coexist. Default: warning. Presets: essential, nuxt, opinionated.

vue/v-bind-style, vue/v-on-style, and vue/v-slot-style enforce directive style preferences. Defaults: warning. Presets: nuxt and/or happy-path, plus opinionated.

vue/valid-attribute-name, vue/valid-v-bind, vue/valid-v-else, vue/valid-v-for, vue/valid-v-if, vue/valid-v-memo, vue/valid-v-model, vue/valid-v-on, vue/valid-v-show, and vue/valid-v-slot report invalid Vue directive syntax. Default: error. Presets: essential, happy-path, nuxt, opinionated.

vue/warn-custom-block and vue/warn-custom-directive warn about custom Vue extension points that need host support or registration. Default: warning. Presets: nuxt, opinionated.