Vue Best Practices
Habits that keep a Vue codebase maintainable as it grows — composables, prop discipline, key usage, and structure.
3 min de lectura
You've now covered the core building blocks of Vue: templates, reactivity, components, routing, and state management. This closing lesson pulls together the habits worth carrying into a real project, most of which you've already seen justified individually earlier in this course.
Extract reusable logic into composables
Any time you find the same ref + watch + lifecycle-hook combination copy-pasted across components, that's a signal to extract it into a composable — a plain function, conventionally named useSomething, that encapsulates reusable reactive logic:
// src/composables/useWindowWidth.js
import { ref, onMounted, onUnmounted } from "vue";
export function useWindowWidth() {
const width = ref(window.innerWidth);
function handleResize() {
width.value = window.innerWidth;
}
onMounted(() => window.addEventListener("resize", handleResize));
onUnmounted(() => window.removeEventListener("resize", handleResize));
return { width };
}<script setup>
import { useWindowWidth } from "../composables/useWindowWidth";
const { width } = useWindowWidth();
</script>This is the Composition API's version of what a mixin tried (and largely failed) to do cleanly in the Options API — one function, explicit inputs and outputs, no hidden merging behavior.
Keep components small and single-purpose
If you can't summarize what a component does in one sentence, it's probably doing too much. Favor several small components with clear props/emits contracts over one large component with many internal conditionals — it's easier to test, easier to reuse, and easier for someone else (including future you) to understand without reading the whole file.
Always key your v-for lists, with a real ID
Covered in depth earlier in this course, but worth repeating as a rule: never rely on the array index as a :key, and never omit :key entirely. Both cause state and DOM nodes to be reused incorrectly when the list changes shape.
Validate props; don't just document them in a comment
// Prefer this
defineProps({
status: {
type: String,
required: true,
validator: (value) => ["idle", "loading", "error", "success"].includes(value),
},
});A validator function catches a bad value the moment a component receives it, with a clear console warning naming the offending prop — far faster to debug than tracing a mysterious rendering bug back to a typo'd string three components away.
Don't mutate props; don't reach for Pinia before you need it
Two rules from earlier lessons worth restating together, because they're really the same principle: keep a single, clear owner for every piece of state. A prop's owner is the parent that passed it — mutate a local copy instead, or emit an event asking the parent to change it. And most state doesn't need to be global — reach for a store only when multiple, non-nested parts of the app genuinely need to read and write the same data; local refs and props are simpler and easier to reason about everywhere else.
Where to go from here
From here, the natural next steps are Nuxt (Vue's full-stack meta-framework, similar in spirit to Next.js), TypeScript integration for stricter prop and store typing, and testing components with Vitest and Vue Test Utils — all built directly on the fundamentals this course covered.