
1. β Use TypeScript for Props & Emits
Why: Ensures type-safety and better DX when consuming or editing the component.
<script setup lang="ts">
interface User {
id: number
name: string
}
const props = defineProps<{
user: User
}>()
const emit = defineEmits<{
(e: 'select', userId: number): void
}>()
</script>
2. π§ Group Logic with Composition API & <script setup>
Why: Clean and structured logic by purpose, not by options.
<script setup lang="ts">
import { ref, computed } from 'vue'
const count = ref(0)
const double = computed(() => count.value * 2)
function increment() {
count.value++
}
</script>
<template>
<button @click="increment">+1</button>
<p>Double: {{ double }}</p>
</template>
3. β»οΈ Extract Reusable Logic into Composables
Why: Keeps components focused and logic shareable.
// composables/useCounter.ts
import { ref } from 'vue'
export function useCounter() {
const count = ref(0)
const increment = () => count.value++
return { count, increment }
}
<!-- MyCounter.vue -->
<script setup lang="ts">
import { useCounter } from '@/composables/useCounter'
const { count, increment } = useCounter()
</script>
<template>
<button @click="increment">Count: {{ count }}</button>
</template>
4. π§© Keep Components Small & Single-Purpose
Why: Improves maintainability and testability.
<!-- AddTodo.vue -->
<script setup lang="ts">
import { ref } from 'vue'
const newTask = ref('')
const emit = defineEmits(['add'])
function submit() {
if (newTask.value) emit('add', newTask.value)
newTask.value = ''
}
</script>
<template>
<input v-model="newTask" placeholder="New task" />
<button @click="submit">Add</button>
</template>
5. π§Ύ Use Interfaces for Local State
Why: Improves type safety and readability.
interface Product {
id: number
name: string
price: number
}
const product = ref<Product>({
id: 1,
name: 'T-shirt',
price: 19.99,
})
6. π Use Computed Properties for Derived State
Why: Keeps templates clean and makes values reactive and cacheable.
const price = ref(100)
const discount = ref(0.2)
const discountedPrice = computed(() => price.value * (1 - discount.value))
<p>Final Price: {{ discountedPrice }}</p>
7. π Handle Async Operations with States
Why: Avoids blank UI, improves error handling and UX.
const data = ref(null)
const loading = ref(false)
const error = ref<string | null>(null)
async function fetchData() {
loading.value = true
error.value = null
try {
const res = await fetch('/api/data')
data.value = await res.json()
} catch (e: any) {
error.value = e.message
} finally {
loading.value = false
}
}
8. πΌ Use Typed Slots for Flexibility & Clarity
Why: Makes slots powerful and predictable.
<!-- BaseList.vue -->
<script setup lang="ts">
defineProps<{
items: Array<{ id: number; name: string }>
}>()
</script>
<template>
<ul>
<li v-for="item in items" :key="item.id">
<slot :item="item">{{ item.name }}</slot>
</li>
</ul>
</template>
<!-- App.vue -->
<BaseList :items="products">
<template #default="{ item }">
<strong>{{ item.name }}</strong>
</template>
</BaseList>
9. π¨ Use CSS Variables for Theming
Why: Simplifies customization and light/dark theme support.
<template>
<div class="card">Welcome to iCreatorStudio</div>
</template>
<style scoped>
.card {
padding: 1rem;
background-color: var(--card-bg);
color: var(--card-text);
border-radius: 0.5rem;
}
</style>
/* Global styles */
:root {
--card-bg: #fff;
--card-text: #333;
}
.dark-theme {
--card-bg: #1e1e1e;
--card-text: #eee;
}
10. π Document Props, Emits, Slots with JSDoc
Why: Boosts dev experience in IDEs and helps future maintainers.
/**
* @prop user - A user object with id and name
*/
const props = defineProps<{
user: { id: number; name: string }
}>()
/**
* @emits delete - Triggers when user is to be deleted
*/
const emit = defineEmits<{
(e: 'delete', id: number): void
}>()
π‘ Final Thoughts
Following these 10 tips will help your Vue components stay:
-
π Reusable
-
π§Ό Clean
-
π οΈ Maintainable
-
π‘ Developer-friendly
Whether you're building a simple widget or a complex dashboard for a client, these small improvements compound into big results. At iCreatorStudio, we believe in crafting products that are polished both on the outside (UI) and inside (code).