🎯

vue-dev

🎯Skill

from wireless25/agentic-coding

VibeIndex|
What it does

Generates modern Vue 3 TypeScript components with best practices, leveraging Composition API, VueUse, Tailwind CSS, and performance-optimized patterns.

πŸ“¦

Part of

wireless25/agentic-coding(8 items)

vue-dev

Installation

Quick InstallInstall with npx
npx skills add https://github.com/wireless25/agentic-coding
Quick InstallInstall with npx
npx skills add https://github.com/wireless25/agentic-coding --skill <skill-name>
Quick InstallInstall with npx
npx skills add <owner/repo>
Quick InstallInstall with npx
npx skills add <owner/repo> --skill <skill-name>
πŸ“– Extracted from docs: wireless25/agentic-coding
1Installs
-
AddedFeb 4, 2026

Skill Details

SKILL.md

"Generate modern, maintainable Vue TypeScript code with best practices: Composition API, script setup with TS, composable patterns over global stores, props destructure, VueUse integration, Tailwind CSS, accessibility, performance optimization, and Vitest testing. Use when creating Vue 3 components, composables, or refactoring Vue code to follow modern patterns."

Overview

# Vue Dev - Modern Vue TypeScript Development

Core Principles

Always follow these principles unless project context demands otherwise:

  • Composition API with

    ```

Composable Patterns

Composable Decision Tree

Create separate composable file when:

  • Logic is reused in 2+ components
  • Logic involves async operations with loading/error states
  • Logic requires complex state management
  • Logic benefits from isolation and testing

Inline composable when:

  • Logic is single-use and simple
  • Logic is tightly coupled to specific component
  • Logic is less than 5 lines

Composable Template Structure

```typescript

// useFeatureName.ts

import { ref, computed, watch } from 'vue'

import { useLocalStorage, useDebounceFn } from '@vueuse/core'

import type { SomeType } from './types'

/**

* Composable for [brief description]

*

* @param param - Description of parameter

* @returns Object with reactive properties and methods

*

* @example

* ```ts

* const { data, loading, error, refresh } = useFeatureName({

* id: '123',

* autoFetch: true

* })

* ```

*/

export function useFeatureName(options: {

id: string

autoFetch?: boolean

debounce?: number

}) {

// State

const data = ref(null)

const loading = ref(false)

const error = ref(null)

// Composables from VueUse

const storage = useLocalStorage(feature-${options.id}, null)

// Methods

const fetchData = async (): Promise => {

loading.value = true

error.value = null

try {

const response = await fetch(/api/data/${options.id})

const result = await response.json() as T

data.value = result

} catch (err) {

error.value = err instanceof Error ? err : new Error('Unknown error')

} finally {

loading.value = false

}

}

const debouncedFetch = useDebounceFn(fetchData, options.debounce ?? 300)

// Computed

const hasData = computed(() => data.value !== null)

const errorMessage = computed(() => error.value?.message ?? '')

// Lifecycle

if (options.autoFetch) {

fetchData()

}

return {

// State

data,

loading,

error,

// Computed

hasData,

errorMessage,

// Methods

refresh: debouncedFetch,

}

}

```

Async Wrapper Pattern

For async operations, prefer wrapper functions that return data/error properties:

```typescript

// useAsync.ts

import { ref, type Ref } from 'vue'

export type AsyncResult = {

data: Ref

loading: Ref

error: Ref

}

/**

* Wrapper for async operations with consistent error handling

*/

export function useAsync(

fn: () => Promise,

options: { immediate?: boolean } = {}

): AsyncResult {

const data = ref(null) as Ref

const loading = ref(false)

const error = ref(null)

const execute = async (): Promise => {

loading.value = true

error.value = null

try {

data.value = await fn()

} catch (err) {

error.value = err instanceof Error ? err : new Error('Unknown error')

} finally {

loading.value = false

}

}

if (options.immediate) {

execute()

}

return {

data,

loading,

error,

}

}

```

State Management Patterns

Nuxt: useState Pattern

```typescript

// composables/useSharedState.ts

export const useCounter = () => {

const counter = useState('counter', () => 0)

const increment = () => {

counter.value++

}

return {

counter: readonly(counter),

increment,

}

}

```

Vite: Global Ref Pattern

```typescript

// store/globalStore.ts

import { ref } from 'vue'

const globalState = ref(0)

export function useGlobalState() {

const increment = () => {

globalState.value++

}

return {

counter: readonly(globalState),

increment,

}

}

```

Provider Pattern (Vite)

```typescript

// context/UserContext.ts

import { provide, inject, type InjectionKey, readonly } from 'vue'

import type { User } from './types'

const UserContextKey: InjectionKey> = Symbol('User')

export function provideUserContext(user: User) {

const state = useUserState(user)

provide(UserContextKey, state)

}

export function useUserContext() {

const context = inject(UserContextKey)

if (!context) {

throw new Error('useUserContext must be used within provideUserContext')

}

return context

}

function useUserState(initialUser: User) {

const currentUser = ref(initialUser)

const setUser = (user: User) => {

currentUser.value = user

}

return {

currentUser: readonly(currentUser),

setUser,

}

}

```

VueUsage Patterns

Always check VueUse first. Common patterns:

Common VueUse Functions

```typescript

import {

// Storage

useLocalStorage,

useSessionStorage,

useStorage,

// DOM

onClickOutside,

onKeyStroke,

useElementBounding,

useWindowSize,

useElementSize,

useIntersectionObserver,

// Async

useAsyncState,

useFetch,

// Utilities

useDebounceFn,

useThrottleFn,

useToggle,

useClipboard,

useTitle,

// Sensors

useMouse,

useScroll,

useMediaQuery,

useNetwork,

// Formatters

useDateFormat,

useTimeAgo,

} from '@vueuse/core'

```

Examples

```typescript

// Close dropdown when clicking outside

const buttonRef = ref()

onClickOutside(buttonRef, () => isOpen.value = false)

// Responsive design

const isMobile = useMediaQuery('(max-width: 768px)')

// Debounced search

const debouncedSearch = useDebounceFn((query: string) => {

// search logic

}, 300)

// Local storage persistence

const theme = useLocalStorage('theme', 'light')

```

TypeScript Patterns

Type Definition

Use type aliases over interfaces:

```typescript

// βœ… Good

type User = {

id: string

name: string

email: string

}

// ❌ Avoid

interface User {

id: string

name: string

email: string

}

```

Props Typing

```typescript

// Simple props

defineProps<{

label: string

count: number

}>()

// Optional props with defaults

const { count = 0 } = defineProps<{

label: string

count?: number

}>()

// Generic props

defineProps<{

items: Array<{ id: string; name: string }>

selected?: string

}>()

```

Emit Typing

```typescript

const emit = defineEmits<{

change: [id: number]

update: [value: string]

}>()

```

Ref Typing

```typescript

// Explicit type

const count = ref(0)

// Inferred from initial value

const message = ref('hello')

// Nullable ref

const user = ref(null)

// Array ref

const items = ref([])

```

Performance Best Practices

Computed vs Watch

```typescript

// βœ… Use computed for derived state

const fullName = computed(() => ${firstName.value} ${lastName.value})

// βœ… Use watch for side effects

watch(fullName, (newName) => {

console.log('Name changed:', newName)

})

```

Shallow Refs for Large Objects

```typescript

// For large objects or frequent reassignments

const largeData = shallowRef([])

```

Lazy Loading Components

```typescript

const Modal = defineAsyncComponent(() => import('./Modal.vue'))

```

v-once for Static Content

```vue

```

v-memo for Expensive Computations

```vue

```

Accessibility Guidelines

Semantic HTML

```vue

```

ARIA Attributes

```vue

```

Keyboard Navigation

```vue

```

Form Accessibility

```vue

```

Testing Patterns

Composable Testing

```typescript

// tests/useFeatureName.spec.ts

import { describe, it, expect, beforeEach } from 'vitest'

import { ref } from 'vue'

import { useFeatureName } from '../useFeatureName'

describe('useFeatureName', () => {

it('should initialize with default values', () => {

const { data, loading, error } = useFeatureName({

id: 'test',

autoFetch: false,

})

expect(data.value).toBeNull()

expect(loading.value).toBe(false)

expect(error.value).toBeNull()

})

it('should fetch data successfully', async () => {

const { data, refresh } = useFeatureName({

id: 'test',

autoFetch: false,

})

await refresh()

expect(data.value).toBeDefined()

})

})

```

File Organization

Always use flat structure:

```

src/

β”œβ”€β”€ components/

β”‚ β”œβ”€β”€ Button.vue

β”‚ β”œβ”€β”€ Input.vue

β”‚ └── Modal.vue

β”œβ”€β”€ composables/

β”‚ β”œβ”€β”€ useAuth.ts

β”‚ β”œβ”€β”€ useFetch.ts

β”‚ └── useForm.ts

β”œβ”€β”€ types/

β”‚ β”œβ”€β”€ user.ts

β”‚ └── api.ts

└── utils/

└── format.ts

```

Code Quality Checklist

Before finalizing any component or composable:

  • [ ] TypeScript strict mode compatible
  • [ ] Props properly typed with defineProps()
  • [ ] Props destructured for defaults
  • [ ] Emits typed with defineEmits()
  • [ ] VueUsed checked and applied where applicable
  • [ ] Tailwind classes used (no