rules/vue/coding-style.md
This file extends common/coding-style.md with Vue specific content.
<script setup lang="ts"> with the Composition API. No Options API in new code..vue file: <script setup>, then <template>, then <style scoped>. One component per file.AuctionCard.vue), composables camelCase prefixed useXxx (useAuctionTimer).eslint-plugin-vue (vue/vue3-recommended). Type-check with vue-tsc.ref is the primary state API. Mutate via .value in script, auto-unwrapped only at template top level.ref inside arrays, Map, or Set still needs .value to read.reactive only for grouped object state. Never reassign a whole reactive object.reactive object or a Pinia store without toRefs / storeToRefs. Plain destructure silently drops reactivity.computed getters must be pure: no side effects, no async, no DOM access.computed only triggers when the returned value changes. Return the prior object unchanged when equal to skip downstream updates.watch is lazy. Pass a getter for a reactive property (watch(() => x.value, ...)), not the bare reactive object.watchEffect is eager and stops tracking dependencies after its first await.setup (onMounted, onUnmounted).onUnmounted.await nextTick().defineProps / defineEmits (tuple form change: [id: number]), defineModel (3.4+) for v-model, withDefaults or 3.5+ reactive-props-destructure for defaults, defineExpose for the public ref API.:key on every v-for, a stable unique primitive. Never the array index, never an object.v-if and v-for on the same element. Wrap with <template v-for> plus an inner v-if, or precompute a filtered list.<script setup lang="ts">
const props = defineProps<{ id: number }>()
const emit = defineEmits<{ change: [id: number] }>()
const open = defineModel<boolean>('open', { default: false })
</script>
frontend-patterns, vite-patterns.