Agent skill
vue-framework
Build reactive user interfaces with Vue 3 using Composition API, reactive state management, and component-based architecture. Master composables, provide/inject, Pinia state management, and Vue Router. Use for: creating interactive single-page applications, implementing reactive data binding and computed properties, building reusable component libraries, managing complex application state, integrating with TypeScript for type safety, optimizing performance with virtual DOM, and developing progressive web applications with Vue ecosystem tools.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/vue-framework
SKILL.md
Vue Framework
Build reactive, component-based user interfaces with Vue 3 Composition API and modern tooling.
Overview
Vue 3 is a progressive JavaScript framework for building user interfaces. The Composition API provides improved code organization, better TypeScript support, and enhanced reusability compared to the Options API. Vue 3 introduces performance improvements, better tree-shaking, and a more flexible reactivity system.
Composition API Fundamentals
Setup Function
<script setup>
import { ref, computed, onMounted } from 'vue';
// Reactive state
const count = ref(0);
const message = ref('Hello Vue!');
// Computed property
const doubleCount = computed(() => count.value * 2);
// Method
function increment() {
count.value++;
}
// Lifecycle hook
onMounted(() => {
console.log('Component mounted');
});
</script>
<template>
<div>
<p>{{ message }}</p>
<p>Count: {{ count }}</p>
<p>Double: {{ doubleCount }}</p>
<button @click="increment">Increment</button>
</div>
</template>
Reactive State
import { ref, reactive, readonly, shallowRef } from 'vue';
// ref - for primitives and objects
const count = ref(0);
const user = ref({ name: 'John', age: 30 });
// Access/modify with .value
count.value++;
user.value.name = 'Jane';
// reactive - for objects only
const state = reactive({
count: 0,
user: { name: 'John' }
});
// Direct access (no .value)
state.count++;
state.user.name = 'Jane';
// readonly - prevent mutations
const readonlyState = readonly(state);
// shallowRef - only top-level reactive
const shallowState = shallowRef({ nested: { value: 1 } });
Computed Properties
import { ref, computed } from 'vue';
const firstName = ref('John');
const lastName = ref('Doe');
// Read-only computed
const fullName = computed(() => `${firstName.value} ${lastName.value}`);
// Writable computed
const fullNameWritable = computed({
get() {
return `${firstName.value} ${lastName.value}`;
},
set(value) {
[firstName.value, lastName.value] = value.split(' ');
}
});
Watchers
import { ref, watch, watchEffect } from 'vue';
const count = ref(0);
const user = ref({ name: 'John' });
// Watch single ref
watch(count, (newVal, oldVal) => {
console.log(`Count changed from ${oldVal} to ${newVal}`);
});
// Watch multiple sources
watch([count, user], ([newCount, newUser], [oldCount, oldUser]) => {
console.log('Multiple values changed');
});
// Watch object property
watch(
() => user.value.name,
(newName) => console.log(`Name: ${newName}`)
);
// watchEffect - auto-tracks dependencies
watchEffect(() => {
console.log(`Count is ${count.value}`);
});
// Cleanup
const stop = watchEffect(() => {
// ...
});
stop(); // Stop watching
Component Design
Props and Emits
<script setup lang="ts">
interface Props {
title: string;
count?: number;
user: { name: string; age: number };
}
interface Emits {
(e: 'update', value: number): void;
(e: 'delete', id: string): void;
}
const props = withDefaults(defineProps<Props>(), {
count: 0
});
const emit = defineEmits<Emits>();
function handleUpdate() {
emit('update', props.count + 1);
}
</script>
<template>
<div>
<h1>{{ title }}</h1>
<p>Count: {{ count }}</p>
<button @click="handleUpdate">Update</button>
</div>
</template>
Slots
<!-- Parent Component -->
<template>
<Card>
<template #header>
<h2>Card Title</h2>
</template>
<template #default>
<p>Card content</p>
</template>
<template #footer>
<button>Action</button>
</template>
</Card>
</template>
<!-- Card Component -->
<script setup>
defineSlots<{
header?: () => any;
default: () => any;
footer?: () => any;
}>();
</script>
<template>
<div class="card">
<div class="card-header">
<slot name="header" />
</div>
<div class="card-body">
<slot />
</div>
<div class="card-footer">
<slot name="footer" />
</div>
</div>
</template>
Provide/Inject
// Parent component
import { provide, ref } from 'vue';
const theme = ref('dark');
provide('theme', theme);
function toggleTheme() {
theme.value = theme.value === 'dark' ? 'light' : 'dark';
}
provide('toggleTheme', toggleTheme);
// Child component (any level deep)
import { inject } from 'vue';
const theme = inject('theme');
const toggleTheme = inject('toggleTheme');
Composables
Creating Composables
// composables/useMouse.ts
import { ref, onMounted, onUnmounted } from 'vue';
export function useMouse() {
const x = ref(0);
const y = ref(0);
function update(event: MouseEvent) {
x.value = event.pageX;
y.value = event.pageY;
}
onMounted(() => window.addEventListener('mousemove', update));
onUnmounted(() => window.removeEventListener('mousemove', update));
return { x, y };
}
// Usage
import { useMouse } from '@/composables/useMouse';
const { x, y } = useMouse();
Composable Patterns
// composables/useFetch.ts
import { ref, watchEffect, toValue } from 'vue';
export function useFetch<T>(url: MaybeRefOrGetter<string>) {
const data = ref<T | null>(null);
const error = ref<Error | null>(null);
const loading = ref(false);
watchEffect(async () => {
loading.value = true;
error.value = null;
try {
const response = await fetch(toValue(url));
data.value = await response.json();
} catch (e) {
error.value = e as Error;
} finally {
loading.value = false;
}
});
return { data, error, loading };
}
// Usage
const { data, error, loading } = useFetch<User>('/api/user');
State Management with Pinia
Defining Stores
// stores/user.ts
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
export const useUserStore = defineStore('user', () => {
// State
const user = ref<User | null>(null);
const isAuthenticated = ref(false);
// Getters
const userName = computed(() => user.value?.name ?? 'Guest');
// Actions
async function login(email: string, password: string) {
const response = await api.login(email, password);
user.value = response.user;
isAuthenticated.value = true;
}
function logout() {
user.value = null;
isAuthenticated.value = false;
}
return { user, isAuthenticated, userName, login, logout };
});
// Usage in component
import { useUserStore } from '@/stores/user';
const userStore = useUserStore();
userStore.login('[email protected]', 'password');
Store Composition
// stores/cart.ts
import { defineStore } from 'pinia';
import { useUserStore } from './user';
export const useCartStore = defineStore('cart', () => {
const userStore = useUserStore();
const items = ref([]);
async function checkout() {
if (!userStore.isAuthenticated) {
throw new Error('Must be logged in');
}
// ... checkout logic
}
return { items, checkout };
});
Vue Router
Route Configuration
// router/index.ts
import { createRouter, createWebHistory } from 'vue-router';
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
component: () => import('@/views/Home.vue')
},
{
path: '/users/:id',
component: () => import('@/views/UserProfile.vue'),
props: true
},
{
path: '/dashboard',
component: () => import('@/views/Dashboard.vue'),
meta: { requiresAuth: true }
}
]
});
// Navigation guard
router.beforeEach((to, from) => {
if (to.meta.requiresAuth && !isAuthenticated()) {
return { path: '/login' };
}
});
export default router;
Using Router in Components
<script setup>
import { useRouter, useRoute } from 'vue-router';
const router = useRouter();
const route = useRoute();
// Access params
const userId = route.params.id;
// Navigate
function goToUser(id: string) {
router.push(`/users/${id}`);
}
// Navigate with query
router.push({ path: '/search', query: { q: 'vue' } });
</script>
Performance Optimization
v-memo
<template>
<div v-for="item in list" :key="item.id" v-memo="[item.id, item.selected]">
<!-- Only re-renders if id or selected changes -->
<p>{{ item.name }}</p>
</div>
</template>
Lazy Loading
// Lazy load components
const AsyncComponent = defineAsyncComponent(() =>
import('./components/Heavy.vue')
);
// With loading/error states
const AsyncComponent = defineAsyncComponent({
loader: () => import('./Heavy.vue'),
loadingComponent: LoadingSpinner,
errorComponent: ErrorDisplay,
delay: 200,
timeout: 3000
});
KeepAlive
<template>
<KeepAlive :max="10">
<component :is="currentView" />
</KeepAlive>
</template>
Using the Reference Files
When to Read Each Reference
/references/composition-api-patterns.md — Read when implementing advanced composables, reactive patterns, or complex state logic.
/references/pinia-guide.md — Read when setting up global state management, store composition, or persistence.
/references/performance-optimization.md — Read when optimizing rendering, implementing virtual scrolling, or reducing bundle size.
/references/testing-guide.md — Read when writing component tests, testing composables, or setting up E2E tests.
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?