All skills
jeffallan avatar

/vue-expert-js

@cfce898
by jeffallanjeffallan/claude-skills12k stars
1,124

Creates Vue 3 components, builds vanilla JS composables, configures Vite projects, and sets up routing and state management using JavaScript only — no TypeScript. Generates JSDoc-typed code with @typedef, @param, and @returns annotations for full type coverage without a TS compiler. Use when building Vue 3 applications with JavaScript only (no TypeScript), when projects require JSDoc-based type hints, when migrating from Vue 2 Options API to Composition API in JS, or when teams prefer vanilla JavaScript, .mjs modules, or need quick prototypes without TypeScript setup.

Use this Skill: https://skilld.dev/gh/jeffallan/claude-skills/vue-expert-js

This session only. Nothing lands on disk.

referencesstate-management.md

≈1.4k tokens on demand. Your agent reads this file only when SKILL.md points to it.

State Management


Setup

// main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

createApp(App).use(createPinia()).mount('#app')

Options Store Syntax

// stores/counter.js
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
    name: 'Counter'
  }),

  getters: {
    doubleCount: (state) => state.count * 2,
    // Getter with parameter
    countPlusN: (state) => (n) => state.count + n
  },

  actions: {
    increment() {
      this.count++
    },
    /** @param {number} amount */
    incrementBy(amount) {
      this.count += amount
    }
  }
})

Setup Store Syntax (Composition API)

// stores/user.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

/**
 * @typedef {Object} User
 * @property {number} id
 * @property {string} name
 * @property {string} email
 */

export const useUserStore = defineStore('user', () => {
  // State
  /** @type {import('vue').Ref<User | null>} */
  const currentUser = ref(null)
  const isLoading = ref(false)
  const error = ref(null)

  // Getters
  const isLoggedIn = computed(() => currentUser.value !== null)
  const userName = computed(() => currentUser.value?.name ?? 'Guest')

  // Actions
  async function login(email, password) {
    isLoading.value = true
    error.value = null
    try {
      const res = await fetch('/api/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, password })
      })
      currentUser.value = (await res.json()).user
      return true
    } catch (e) {
      error.value = e.message
      return false
    } finally {
      isLoading.value = false
    }
  }

  function logout() {
    currentUser.value = null
  }

  return { currentUser, isLoading, error, isLoggedIn, userName, login, logout }
})

Using Stores

<script setup>
import { useUserStore } from '@/stores/user'
import { storeToRefs } from 'pinia'

const userStore = useUserStore()

// Use storeToRefs for reactive state/getters
const { currentUser, isLoggedIn, isLoading } = storeToRefs(userStore)

// Actions can be destructured directly
const { login, logout } = userStore
</script>

<template>
  <div v-if="isLoading">Loading...</div>
  <div v-else-if="isLoggedIn">
    Welcome, {{ currentUser?.name }}
    <button @click="logout">Logout</button>
  </div>
</template>

Store Composition

// stores/cart.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { useProductsStore } from './products'
import { useUserStore } from './user'

export const useCartStore = defineStore('cart', () => {
  const items = ref([]) // [{ productId, quantity }]

  // Access other stores
  const productsStore = useProductsStore()
  const userStore = useUserStore()

  const total = computed(() =>
    items.value.reduce((sum, item) => {
      const product = productsStore.items.find(p => p.id === item.productId)
      return sum + (product?.price ?? 0) * item.quantity
    }, 0)
  )

  function addItem(productId, quantity = 1) {
    const existing = items.value.find(i => i.productId === productId)
    if (existing) existing.quantity += quantity
    else items.value.push({ productId, quantity })
  }

  async function checkout() {
    if (!userStore.isLoggedIn) throw new Error('Must be logged in')
    await fetch('/api/checkout', {
      method: 'POST',
      body: JSON.stringify({ userId: userStore.currentUser.id, items: items.value })
    })
    items.value = []
  }

  return { items, total, addItem, checkout }
})

Persistence

// stores/settings.js
import { defineStore } from 'pinia'
import { ref, watch } from 'vue'

const STORAGE_KEY = 'app-settings'

function loadFromStorage() {
  try {
    return JSON.parse(localStorage.getItem(STORAGE_KEY)) ?? {}
  } catch {
    return {}
  }
}

export const useSettingsStore = defineStore('settings', () => {
  const saved = loadFromStorage()

  const theme = ref(saved.theme ?? 'light')
  const language = ref(saved.language ?? 'en')

  watch([theme, language], () => {
    localStorage.setItem(STORAGE_KEY, JSON.stringify({
      theme: theme.value,
      language: language.value
    }))
  })

  return { theme, language }
})

Testing Stores

// stores/__tests__/counter.test.js
import { describe, it, expect, beforeEach } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'
import { useCounterStore } from '../counter'

describe('Counter Store', () => {
  beforeEach(() => setActivePinia(createPinia()))

  it('increments count', () => {
    const store = useCounterStore()
    store.increment()
    expect(store.count).toBe(1)
  })

  it('computes double count', () => {
    const store = useCounterStore()
    store.count = 5
    expect(store.doubleCount).toBe(10)
  })
})

Quick Reference

Feature Options Syntax Setup Syntax
State state: () => ({}) const x = ref()
Getter getters: { x: (state) => } const x = computed()
Action actions: { fn() {} } function fn() {}
Use in component storeToRefs() for state Same
Reset state store.$reset() Manual reset function
Subscribe store.$subscribe((mutation, state) => {}) Same
Other stores Use in actions Call at setup top level

Source: SKILL.md on GitHub

1 alert16d5 checks · Risk CRITICAL
  • Gen Agent Trust Hub16d

    The skill is safe to use. Automated scanner alerts regarding the documentation URLs are false positives; the URL belongs to the author's official GitHub Pages domain and serves as standard project reference material. There are no security risks, malicious scripts, or data exfiltration behaviors present.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer6mo

    6 files scanned · No issues

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at cfce898. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Steadyupdated 2 months ago
Other metadata
metadata
{
  "author": "https://github.com/Jeffallan",
  "version": "1.1.0",
  "domain": "frontend",
  "triggers": "Vue JavaScript, Vue without TypeScript, Vue JSDoc, Vue JS only, Vue vanilla JavaScript, .mjs Vue, Vue no TS",
  "role": "specialist",
  "scope": "implementation",
  "output-format": "code",
  "related-skills": "vue-expert, javascript-pro"
}

README badge

README badge for jeffallan/claude-skills/vue-expert-js