| name | vue-development |
| description | Vue 3 development with Composition API, reactivity system, component patterns, TypeScript integration, and best practices. |
| allowed-tools | Read, Write, Edit, Bash, Glob, Grep |
| graph | {"domains":["domain:web-development"],"specializations":["specialization:web-development"],"skillAreas":["skill-area:vue-components","skill-area:application-state-management"],"roles":["role:frontend-engineer"],"topics":["topic:component-based-architecture"]} |
Vue Development Skill
Expert assistance for building Vue 3 applications with Composition API and modern patterns.
Capabilities
- Create Vue 3 components with Composition API
- Implement reactive state with ref and reactive
- Build composables for reusable logic
- Configure TypeScript with Vue
- Set up Vue Router and navigation guards
- Implement provide/inject for dependency injection
Usage
Invoke this skill when you need to:
- Create Vue 3 components
- Build composables for shared logic
- Set up Vue project structure
- Implement reactive patterns
- Configure Vue with TypeScript
Inputs
| Parameter | Type | Required | Description |
|---|
| componentName | string | Yes | Component name (PascalCase) |
| compositionApi | boolean | No | Use Composition API (default: true) |
| typescript | boolean | No | Use TypeScript (default: true) |
| scriptSetup | boolean | No | Use script setup (default: true) |
Configuration Example
{
"componentName": "UserProfile",
"compositionApi": true,
"typescript": true,
"scriptSetup": true,
"features": ["props", "emits", "slots"]
}
Component Patterns
Script Setup Component
<!-- components/UserProfile.vue -->
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';
interface User {
id: string;
name: string;
email: string;
avatar: string;
}
// Props with defaults
const props = withDefaults(defineProps<{
user: User;
editable?: boolean;
}>(), {
editable: false,
});
// Emits with typing
const emit = defineEmits<{
update: [user: User];
delete: [id: string];
}>();
// Reactive state
const isEditing = ref(false);
const editedName = ref(props.user.name);
// Computed
const initials = computed(() => {
return props.user.name
.split(' ')
.map(n => n[0])
.join('')
.toUpperCase();
});
// Methods
function saveChanges() {
emit('update', { ...props.user, name: editedName.value });
isEditing.value = false;
}
// Lifecycle
onMounted(() => {
console.log('UserProfile mounted');
});
// Expose to parent
defineExpose({
resetForm: () => {
editedName.value = props.user.name;
isEditing.value = false;
},
});
</script>
<template>
<div class="user-profile">
<div class="avatar">
<img v-if="user.avatar" :src="user.avatar" :alt="user.name" />
<span v-else class="initials">{{ initials }}</span>
</div>
<div class="info">
<template v-if="isEditing">
<input v-model="editedName" @keyup.enter="saveChanges" />
<button @click="saveChanges">Save</button>
<button @click="isEditing = false">Cancel</button>
</template>
<template v-else>
<h2>{{ user.name }}</h2>
<p>{{ user.email }}</p>
<button v-if="editable" @click="isEditing = true">Edit</button>
</template>
</div>
<slot name="actions" :user="user" />
</div>
</template>
<style scoped>
.user-profile {
display: flex;
gap: 1rem;
padding: 1rem;
}
.avatar {
width: 64px;
height: 64px;
border-radius: 50%;
overflow: hidden;
}
.initials {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
background: #e5e7eb;
font-weight: bold;
}
</style>
Composable Pattern
import { ref, computed, readonly } from 'vue';
interface User {
id: string;
name: string;
email: string;
}
export function useUser(userId: string) {
const user = ref<User | null>(null);
const loading = ref(false);
const error = ref<Error | null>(null);
const isAuthenticated = computed(() => !!user.value);
async function fetchUser() {
loading.value = true;
error.value = null;
try {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) throw new Error('Failed to fetch user');
user. = response.();
} (e) {
error. = e ;
} {
loading. = ;
}
}
() {
(!user.) ;
loading. = ;
{
response = (, {
: ,
: { : },
: .(data),
});
user. = response.();
} (e) {
error. = e ;
} {
loading. = ;
}
}
{
: (user),
: (loading),
: (error),
isAuthenticated,
fetchUser,
updateUser,
};
}
{ ref, watch } ;
useLocalStorage<T>(: , : T) {
stored = .(key);
data = ref<T>(stored ? .(stored) : defaultValue);
(
data,
{
.(key, .(newValue));
},
{ : }
);
data;
}
Provide/Inject Pattern
import { provide, inject, ref, type Ref, type InjectionKey } from 'vue';
type Theme = 'light' | 'dark';
interface ThemeContext {
theme: Ref<Theme>;
toggleTheme: () => void;
}
const ThemeKey: InjectionKey<ThemeContext> = Symbol('theme');
export function provideTheme() {
const theme = ref<Theme>('light');
function toggleTheme() {
theme.value = theme.value === 'light' ? 'dark' : 'light';
}
provide(ThemeKey, { theme, toggleTheme });
return { theme, toggleTheme };
}
export function useTheme() {
const context = inject(ThemeKey);
if (!context) {
();
}
context;
}
Vue Router Setup
import { createRouter, createWebHistory } from 'vue-router';
import type { RouteRecordRaw } from 'vue-router';
const routes: RouteRecordRaw[] = [
{
path: '/',
component: () => import('@/layouts/DefaultLayout.vue'),
children: [
{
path: '',
name: 'home',
component: () => import('@/views/Home.vue'),
},
{
path: 'dashboard',
name: 'dashboard',
component: () => import('@/views/Dashboard.vue'),
meta: { requiresAuth: true },
},
],
},
{
path: '/login',
name: 'login',
component: () => import('@/views/Login.vue'),
},
];
const router = createRouter({
history: createWebHistory(),
routes,
});
router.( {
isAuthenticated = !!.();
(to.. && !isAuthenticated) {
({ : , : { : to. } });
} {
();
}
});
router;
Best Practices
- Use script setup for cleaner components
- Create composables for reusable logic
- Type props and emits properly
- Use readonly for exposed reactive state
- Leverage provide/inject for deep dependency passing
Target Processes
- vue-application-development
- vue-component-library
- nuxt-full-stack
- frontend-architecture