SFC Authoring Rules
10.4.1. Block Order
Blocks within an SFC (Single File Component) must follow this order:
<script setup lang="ts">— Logic<template>— Markup<style scoped>— Styles
vue
<script setup lang="ts">
import { ref } from 'vue'
const message = ref('Hello')
</script>
<template>
<p>{{ message }}</p>
</template>
<style scoped>
p {
color: inherit;
}
</style>Required
The ESLint vue/block-order rule enforces this order. Violations will trigger a lint error.
10.4.2. Template Authoring Rules
v-htmlmust not be used. It is vulnerable to XSS attacks. If unavoidable, sanitize the content with DOMPurify before use.v-formust always have a:keybinding. Do not useindexas the key.v-ifandv-formust not be used on the same element. Separate them using a<template>tag.
vue
<!-- Correct -->
<template v-for="item in items" :key="item.id">
<div v-if="item.isActive">
{{ item.name }}
</div>
</template>
<!-- Incorrect -->
<div v-for="item in items" v-if="item.isActive" :key="item.id">
{{ item.name }}
</div>10.4.3. Attribute Binding Rules
- Static attributes must be written as plain HTML attributes; dynamic attributes must use the
v-bind(:) shorthand. - Boolean attributes must be declared without a value to pass
true. - When there are 3 or more attributes, place one attribute per line.
vue
<!-- 3 or more attributes — one per line -->
<UserCard
:user="currentUser"
:editable="isAdmin"
:loading="isLoading"
@update="handleUpdate"
/>
<!-- Boolean attributes -->
<BaseButton
primary
disabled
@click="handleClick"
/>10.4.4. Event Handling
- Event handlers must use the
@shorthand. Do not usev-on. - Inline handlers are permitted only for simple expressions (single line).
- Complex logic must be extracted into a separate function.
vue
<script setup lang="ts">
function handleSubmit() {
// Complex logic
}
</script>
<template>
<!-- Correct: simple expression -->
<button @click="count++">Increment</button>
<!-- Correct: function reference -->
<form @submit.prevent="handleSubmit">
<!-- ... -->
</form>
</template>10.4.5. Style Rules
- The
<style>tag must always usescoped. Global styles must be managed in thesrc/styles/directory. - The
:deep()selector should be used only sparingly, limited to overriding child component styles. - Tailwind CSS utility classes should be used preferentially; custom CSS must be minimized.
vue
<style scoped>
/* Scoped styles */
.container {
@apply flex items-center gap-4;
}
/* Child component style override (limited use) */
:deep(.child-class) {
color: inherit;
}
</style>