Vize

Vue Rule Options

Vue rule option examples cover component name casing, prop mutation depth, SFC block order, self-closing tags, event names, and attribute casing. The main Rule Options page lists the complete option table, unknown-field behavior, and scoped replacement semantics. Declared prop casing is documented in Prop Rule Options.

vue/html-quotes

Use "double" or "single" as a compact quote-style option. The object form also accepts avoidEscape; when it is true, an already quoted attribute containing the requested delimiter keeps its existing quotes instead of adding an escape.

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

export default defineConfig({}).withVue({
  linter: {
    rules: { "vue/html-quotes": "warn" },
    ruleOptions: {
      "vue/html-quotes": { style: "single", avoidEscape: true },
    },
  },
});

Bad with this config:

<div class="card"></div>

Good with this config:

<div class="card" title="don't"></div>

The first value is safely fixed to class='card'; title="don't" remains unchanged. Options configure the rule without enabling it.

vue/mustache-interpolation-spacing

Use "always" to require whitespace immediately inside both mustache delimiters, or "never" to require tight interpolation delimiters. The default is "always".

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

export default defineConfig({}).withVue({
  linter: {
    rules: { "vue/mustache-interpolation-spacing": "warn" },
    ruleOptions: { "vue/mustache-interpolation-spacing": "never" },
  },
});

Bad with this config:

<p>{{ message }}</p>

Good with this config:

<p>{{message}}</p>

Both delimiter-adjacent spaces are reported and safely removed. Options configure the rule without enabling it.

vue/valid-v-slot

By default, static and dynamic slot arguments reject modifiers such as #item.memo and #[name].memo. Enable allowModifiers when a component API uses dotted slot names, as Vuetify data tables do:

import { defineConfig } from "vize/config";

export default defineConfig({
  linter: {
    rules: { "vue/valid-v-slot": "error" },
    ruleOptions: {
      "vue/valid-v-slot": { allowModifiers: true },
    },
  },
});

Allowed with this config:

<FancyPanel>
  <template #item.memo="{ item }">{{ item.memo }}</template>
  <template #[name].memo="slot">{{ slot }}</template>
</FancyPanel>

An argless v-slot.memo remains invalid with either setting. A dynamic name such as #[slot.name]="slot" also remains invalid: the name is evaluated before that slot's parameters exist. The option only changes modifier policy and does not enable a disabled rule. An explicit allowModifiers: false in a matching entry restores the default policy even when the root config allows modifiers.

vue/component-name-in-template-casing

Use this option when a project wants component tags in templates to be consistently PascalCase or kebab-case. By default, it checks registered component names from <script setup> declarations and direct components registrations. Unregistered tags and scriptless templates stay clear. Use registeredComponentsOnly: false to check all custom tags, or globals: ["PrimaryButton"] for explicitly registered global components. Global names are literal strings; regex patterns and the ESLint ignores option remain unsupported. Native HTML, SVG, MathML, slot and dynamic component tags are skipped.

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

export default defineConfig({}).withVue({
  linter: {
    rules: {
      "vue/component-name-in-template-casing": "warn",
    },
    ruleOptions: {
      "vue/component-name-in-template-casing": {
        casing: "kebab-case",
      },
    },
  },
});

Bad with the config above:

<script setup>
import PrimaryButton from "./PrimaryButton.vue";
</script>
<template>
  <PrimaryButton />
</template>

Good:

<script setup>
import PrimaryButton from "./PrimaryButton.vue";
</script>
<template>
  <primary-button />
</template>

vue/no-mutating-props

Use shallowOnly when a migration allows nested prop mutation for now but still forbids replacing the prop binding itself. The default false reports both direct and nested mutations.

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

export default defineConfig({}).withVue({
  linter: {
    rules: {
      "vue/no-mutating-props": "error",
    },
    ruleOptions: {
      "vue/no-mutating-props": {
        shallowOnly: true,
      },
    },
  },
});

Bad with the config above:

<script setup>
const props = defineProps<{ count: number; settings: { dense: boolean } }>();

props.count = 2;
</script>

Good with the config above:

<script setup>
const props = defineProps<{ settings: { dense: boolean } }>();

props.settings.dense = true;
</script>

vue/sfc-element-order

Use this option when a project has a fixed SFC block order. A string is one rank; a nested array means any listed block selector may appear at that rank. Supported built-in selectors are script, script:not([setup]), script[setup], template, and style; other non-empty strings match custom block names.

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

export default defineConfig({}).withVue({
  linter: {
    rules: {
      "vue/sfc-element-order": "warn",
    },
    ruleOptions: {
      "vue/sfc-element-order": {
        order: ["template", "script:not([setup])", "script[setup]", "i18n", "style"],
      },
    },
  },
});

Bad with the config above:

<style scoped></style>
<template></template>
<script setup></script>

Good:

<template></template>
<script setup></script>
<style scoped></style>

vue/html-self-closing

Use this option to choose self-closing style per element family. always requires empty elements to self-close, never forbids self-closing tags, and any accepts both forms. Omitted nested fields keep the Vize defaults.

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

export default defineConfig({}).withVue({
  linter: {
    rules: {
      "vue/html-self-closing": "warn",
    },
    ruleOptions: {
      "vue/html-self-closing": {
        html: {
          void: "always",
          normal: "never",
          component: "always",
        },
        svg: "always",
        math: "always",
      },
    },
  },
});

Bad with the config above:

<template>
  <div />
  <PrimaryButton></PrimaryButton>
</template>

Good:

<template>
  <div></div>
  <PrimaryButton />
</template>

vue/v-on-event-hyphenation

Use this option for static custom event listener names on components. always reports camelCase listener arguments such as @saveItem; never reports hyphenated listener arguments. Native HTML events, object syntax, and dynamic arguments are skipped.

See event listener options for ignore-list validation and overrides.

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

export default defineConfig({}).withVue({
  linter: {
    rules: {
      "vue/v-on-event-hyphenation": "warn",
    },
    ruleOptions: {
      "vue/v-on-event-hyphenation": { style: "always", ignore: ["update:"] },
    },
  },
});

Bad with the config above:

<template>
  <PrimaryButton @saveItem="save" />
</template>

Good:

<template>
  <PrimaryButton @save-item="save" />
  <button @saveItem="save" />
</template>

vue/v-bind-style

Use this option to choose the authored binding-directive spelling. The default shorthand style requires :class; longform requires v-bind:class. Both modes preserve arguments, modifiers, and values when applying a fix. Longform also rewrites .title to v-bind:title.prop, retaining explicit modifiers and their order. Object syntax such as v-bind="attrs" is left unchanged.

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

export default defineConfig({}).withVue({
  linter: {
    rules: {
      "vue/v-bind-style": "warn",
    },
    ruleOptions: {
      "vue/v-bind-style": "longform",
    },
  },
});

Bad with the config above:

<a :title.camel="title">Open</a>

Good:

<a v-bind:title.camel="title" v-bind="attrs">Open</a>

vue/v-on-style

Use this option to choose the authored event-directive spelling. The default shorthand style requires @click; longform requires v-on:click. Both modes preserve arguments and modifiers when applying a fix. Object syntax such as v-on="listeners" has no interchangeable shorthand and is therefore left unchanged.

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

export default defineConfig({}).withVue({
  linter: {
    rules: {
      "vue/v-on-style": "warn",
    },
    ruleOptions: {
      "vue/v-on-style": "longform",
    },
  },
});

Bad with the config above:

<button @click.stop="save">Save</button>

Good:

<button v-on:click.stop="save" v-on="listeners">Save</button>

vue/v-slot-style

Choose one spelling for every slot with "shorthand" or "longform", or use the object form to set the component default slot, <template> default slot, and named slots independently. The defaults are { atComponent: "v-slot", default: "shorthand", named: "shorthand" }. The bare "v-slot" value is valid for atComponent and default, but not for named, because dropping a named slot argument would change behavior. Safe fixes preserve slot arguments and values.

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

export default defineConfig({}).withVue({
  linter: {
    rules: {
      "vue/v-slot-style": "warn",
    },
    ruleOptions: {
      "vue/v-slot-style": {
        atComponent: "longform",
        default: "v-slot",
        named: "longform",
      },
    },
  },
});

Bad with the config above:

<MyPanel #default="slot"><template #header>Header</template>{{ slot.item }}</MyPanel>

Good:

<MyPanel v-slot:default="slot"><template v-slot:header>Header</template>{{ slot.item }}</MyPanel>

Attribute naming options {#vue-attribute-hyphenation}

See attribute styles and literal exemptions.