mcpField
V
vue-composition

vue-composition

github.com/claude-dev-suiteDevelopment4636 installsUpdated 2d ago
Repository

About vue-composition

A reference skill for Vue 3's Composition API, aimed at developers building Vue applications. It explains reactivity and composables, with guidance on security, errors, performance, accessibility, and testing.

Key features

  1. 01Reactivity APIs and state
  2. 02Composable function patterns
  3. 03Component and prop practices
  4. 04Security and error handling
  5. 05Performance and accessibility guidance
  6. 06Testing setup

Use cases

  1. 01Build reusable composables
  2. 02Manage reactive component state
  3. 03Review Vue app security and performance

MCP support

The skill points to `mcp__documentation__fetch_docs` for comprehensive Vue documentation.

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/claude-dev-suite/claude-dev-suite --skill vue-composition

After installing, type/vue-compositionin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

advanced.md
quick-ref/
composables.md
reactivity-cheatsheet.md

Details

From the project README

Vue 3 Composition API

Full Reference: See advanced.md for WebSocket composable, provide/inject plugin pattern, Socket.IO integration, and room management.

Deep Knowledge: Use mcp__documentation__fetch_docs with technology: vue for comprehensive documentation.

When NOT to Use This Skill

Skip this skill when:

  • Working with Vue 2 Options API (use legacy Vue docs)
  • Building React applications (use frontend-react)
  • Using Angular framework (use angular)
  • Working with Svelte (use svelte)
  • Dealing with server-side only logic (no framework needed)

Component Structure

<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'

interface Props {
  title: string
  count?: number
}

const props = defineProps<Props>()
const emit = defineEmits<{
  update: [value: string]
}>()

const localState = ref('')
const doubled = computed(() => props.count * 2)

onMounted(() => {
  console.log('Component mounted')
})
</script>

<template>
  <div>
    <h1>{{ title }}</h1>
    <p>{{ doubled }}</p>
  </div>
</template>

Reactivity System

APIPurpose
ref()Primitive reactive value
reactive()Reactive object
computed()Derived state
watch()Watch reactive sources
watchEffect()Auto-track dependencies

Composables Pattern

// useCounter.ts
export function useCounter(initial = 0) {
  const count = ref(initial)
  const increment = () => count.value++
  const decrement = () => count.value--
  return { count, increment, decrement }
}

Key Concepts

  • <script setup> is recommended syntax
  • Use ref for primitives, reactive for objects
  • v-model for two-way binding
  • Slots for content distribution

Anti-Patterns

Anti-PatternWhy It's BadCorrect Approach
Using reactive() for primitivesLoses reactivity on destructureUse ref() for primitives
Mutating props directlyBreaks one-way data flowEmit events, use v-model
Using v-html without sanitizationXSS vulnerabilityUse DOMPurify before rendering
Large computed without memoRecalculates on every renderBreak into smaller computeds
Not cleaning up in onUnmountedMemory leaksClear timers, unsubscribe
Using watch when computed sufficesUnnecessary complexityUse computed for derived state

Quick Troubleshooting

IssueLikely CauseSolution
Computed not updatingForgot .value on refAccess refs with .value
Template not reactiveUsed let instead of refConvert to ref() or reactive()
Props mutation warningDirectly modifying propsClone props or emit update event
Component not re-renderingUsing reactive on primitiveUse ref() for primitives
Memory leaksForgot to cleanupAdd cleanup in onUnmounted
v-model not workingWrong event nameUse update:modelValue event

Production Readiness

Security Best Practices
<script setup lang="ts">
// NEVER use v-html with user input without sanitization
import DOMPurify from 'dompurify'

const props = defineProps<{ userContent: string }>()
const sanitizedContent = computed(() => DOMPurify.sanitize(props.userContent))
</script>

<template>
  <!-- BAD -->
  <div v-html="userContent" />

  <!-- GOOD -->
  <div v-html="sanitizedContent" />
</template>
// Validate external URLs
const isValidUrl = (url: string): boolean => {
  try {
    const parsed = new URL(url)
    return ['http:', 'https:'].includes(parsed.protocol)
  } catch {
    return false
  }
}

// Never expose secrets in client code
// Use runtime config or server routes instead
const config = useRuntimeConfig()
// config.public.* is safe for client
// config.* (without public) stays server-side
Error Handling
<script setup lang="ts">
import { onErrorCaptured } from 'vue'

// Component-level error boundary
onErrorCaptured((error, instance, info) => {
  // Log to error tracking service
  logError(error, { component: instance?.$options.name, info })

  // Return false to prevent error propagation
  return false
})
</script>
// Global error handler (main.ts)
const app = createApp(App)

app.config.errorHandler = (error, instance, info) => {
  console.error('Global error:', error)
  // Send to error tracking (Sentry, etc.)
  captureException(error, { extra: { info } })
}

app.config.warnHandler = (msg, instance, trace) => {
  // Log warnings in development
  if (import.meta.env.DEV) console.warn(msg, trace)
}
Performance Optimization
<script setup lang="ts">
import { defineAsyncComponent, shallowRef } from 'vue'

// Lazy load heavy components
const HeavyChart = defineAsyncComponent({
  loader: () => import('./HeavyChart.vue'),
  loadingComponent: LoadingSpinner,
  delay: 200,
  errorComponent: ErrorDisplay,
})

// Use shallowRef for large objects that don't need deep reactivity
const largeDataset = shallowRef<DataItem[]>([])

// Computed with getter/setter for derived state
const filteredItems = computed(() =>
  items.value.filter(item => item.active)
)
</script>

<template>
  <!-- Use v-once for static content -->
  <header v-once>
    <h1>{{ appTitle }}</h1>
  </header>

  <!-- Use v-memo for expensive list items -->
  <div v-for="item in list" :key="item.id" v-memo="[item.id, item.updated]">
    <ExpensiveComponent :data="item" />
  </div>

  <!-- Virtual scrolling for large lists -->
  <VirtualList :items="largeDataset" :item-height="50" />
</template>
Accessibility (a11y)
<template>
  <!-- Use semantic HTML -->
  <button @click="handleClick">Submit</button>

  <!-- ARIA for dynamic content -->
  <div role="alert" aria-live="polite" v-if="error">
    {{ error }}
  </div>

  <!-- Focus management -->
  <dialog ref="dialogRef" @vue:mounted="dialogRef?.focus()">
    <h2 id="dialog-title">Confirm Action</h2>
    <div aria-labelledby="dialog-title">...</div>
  </dialog>
</template>
Testing Setup
// Component testing with Vue Test Utils
import { mount } from '@vue/test-utils'
import { describe, it, expect, vi } from 'vitest'

describe('UserForm', () => {
  it('emits submit with form data', async () => {
    const wrapper = mount(UserForm)

    await wrapper.find('input[name="email"]').setValue('[email protected]')
    await wrapper.find('form').trigger('submit')

    expect(wrapper.emitted('submit')).toBeTruthy()
    expect(wrapper.emitted('submit')[0]).toEqual([{ email: '[email protected]' }])
  })
})
Monitoring Metrics
MetricAlert Threshold
Largest Contentful Paint (LCP)> 2.5s
First Input Delay (FID)> 100ms
Cumulative Layout Shift (CLS)> 0.1
JavaScript bundle size> 200KB (gzipped)
Component render time> 16ms
Build Optimization
// vite.config.ts
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          vue: ['vue', 'vue-router', 'pinia'],
          ui: ['@headlessui/vue', '@vueuse/core'],
        },
      },
    },
    sourcemap: true,
  },
})
Checklist
  • Global error handler configured
  • No sensitive data in client state
  • DOMPurify for v-html content
  • Async components for code splitting
  • shallowRef for large non-reactive data
  • v-memo for expensive list rendering
  • Virtual scrolling for long lists
  • Semantic HTML and ARIA labels
  • Core Web Vitals monitored
  • Bundle size optimized
  • Error reporting service integrated

Reference Documentation

Deep Knowledge: Use mcp__documentation__fetch_docs with technology: vue for comprehensive documentation.

Comments

Markdown supported