Vue 3 authoring/review: components, composables, reactivity, Pinia, Pinia Colada, file routing, forms. Composition API + script setup. Auto-activates on .vue files. Deep review: mk:vue-best-practices.
Scanned 9/6/2026
Install to Claude Code
npx -y skills add ngocsangyem/MeowKit --skill mk-vue --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Mk Vue?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/ngocsangyem-mk-vue-meowkit)More formats (shields.io, HTML) on the badges page.
---
name: "mk-vue"
description: "Vue 3 authoring/review: components, composables, reactivity, Pinia, Pinia Colada, file routing, forms. Composition API + script setup. Auto-activates on .vue files. Deep review: mk:vue-best-practices."
---
# Vue 3
Vue 3 patterns — Composition API + `<script setup lang="ts">`, Pinia, Pinia
Colada, file-based routing, and performance. Single entrypoint routing to focused references.
> Use `npx chub search vue` for relevant documentation packages within the Context Hub.
## When to Use
**Auto-activate on:** `.vue` files, Vue 3 Composition API, `<script setup>`, Pinia stores,
Pinia Colada queries/mutations, Vue Router file-based routes, composables.
**Explicit:** `the vue skill [concern]`
**Deep best practices:** for a thorough best-practices **review/recommendations** pass or the
full ordered workflow — built-in components (Teleport/Suspense/KeepAlive/Transition),
animation techniques, optional features, and the performance pass — use `mk:vue-best-practices`.
This skill stays the everyday quick-reference.
**Do NOT invoke for:** deep best-practices review/workflow (use `mk:vue-best-practices`),
TypeScript fundamentals (use `mk:typescript`), React (use `mk:react-patterns`), Angular (use
`mk:angular`), visual design (use `mk:frontend-design`), testing (use `mk:testing` / `mk:qa`;
Vue test design/review → `mk:vue-testing-best-practices`).
## Core Rules (always apply)
Follow the repository's established conventions first. When it has no convention, use
these defaults; project requirements may override them. Everything else is in the references.
- **`<script setup lang="ts">` only** — never Options API, never `defineComponent()` wrapper.
- **`type` over `interface`** for defining object/prop shapes; keep types alongside the code.
- **Arrow functions for methods and callbacks.**
- **Named exports over default exports — except composables, which use `export default`.**
- **PascalCase component names, kebab-case file names** (`UserProfile` in
`user-profile.vue`); composables are `camelCase` with `use` prefix (`useAuth`).
- **Name general → specific:** `SearchButtonClear.vue`, not `ClearSearchButton.vue`.
- **TailwindCSS classes, not manual CSS;** never hard-code colors — use the Tailwind color system.
- **Comments explain _why_, not _what_.**
- **`ref()` for primitives, `reactive()` only for complex objects;** `computed()` for derived state.
- **Never `v-html` with user-provided content** — XSS vector (see `mk:` security rules).
## Data Layer (conditional default)
When the project depends on `@pinia/colada`, it is the **preferred async/data-fetching layer**
— see `references/pinia-colada.md`. Otherwise use plain Pinia stores + composables for state.
Pinia stores hold global UI/app state; data fetching belongs in Pinia Colada when present.
## When to Read Each Reference
Read one level deep from this file. Read multiple when a task spans topics.
| Task involves | Read |
| ---------------------------------------------------------------------- | -------------------------------------- |
| Components, props/emits, slots, `defineModel`, naming, templates | `references/components.md` |
| Composables (`use*`), shared reactive logic | `references/composables.md` |
| `ref`/`reactive`/`computed`/`watch`, `toRefs`, performance | `references/reactivity-performance.md` |
| Global state with Pinia setup stores, `storeToRefs` | `references/state-pinia.md` |
| Pinia Colada core: keys, `defineQueryOptions`, `useQuery`, mutations | `references/pinia-colada.md` |
| Optimistic updates, infinite/paginated queries, SSR, cancellation | `references/pinia-colada-advanced.md` |
| Pinia Colada plugins (retry, delay, auto-refetch, persister, hooks) | `references/pinia-colada-plugins.md` |
| Direct cache access: `getQueryData`/`setQueryData`/`invalidateQueries` | `references/pinia-colada-cache.md` |
| File-based routing, route groups, params, `definePage`, typed router | `references/routing-pages.md` |
| Project stack, structure, commands, conventions, docs research | `references/project-standards.md` |
| 3.4/3.5 core APIs: `useId`, `nextTick`, flush timing, `watch` once/deep, `onWatcherCleanup` | `references/core-new-apis.md` |
| `<script setup>` compiler macros — `defineOptions`, `withDefaults`, generic components | `references/script-setup-macros.md` |
| Advanced reactivity: `effectScope`, `customRef`, `triggerRef`, `markRaw`, `shallowReadonly` | `references/advanced-patterns.md` |
## Gotchas
- **Destructuring `reactive()` loses reactivity** — `const { count } = reactive({ count: 0 })`
makes `count` a plain number; keep the reactive object intact, or use `toRefs()`, or `ref()`.
- **`storeToRefs()` required when destructuring Pinia state** — `const { user } = useAuthStore()`
gives a non-reactive snapshot; use `const { user } = storeToRefs(useAuthStore())`. Methods
are destructured directly from the store (not through `storeToRefs`).
- **Parent `ref` access needs `defineExpose()`** — `childRef.value.method()` returns `undefined`
unless the child `<script setup>` explicitly `defineExpose({ method })`.
- **Use `:slotted()` not `:deep()` for slotted content** — slot content comes from the parent
scope, so `:deep(.child-class)` in a scoped style block does not match it; use `:slotted(.child-class)`.
- **`watchEffect` cleanup race on fast re-renders** — register cleanup to cancel async work:
`watchEffect((onCleanup) => { onCleanup(() => controller.abort()) })`, or a fast prop change
starts a second effect before the first resolves and writes stale state.
- **Pinia store not hydrated in SSR** — calling `useMyStore()` outside a component setup context
(e.g. a top-level module) creates an instance disconnected from the SSR app; call stores inside
`setup()` or pass the `pinia` instance explicitly: `useMyStore(pinia)`.
- **Query keys must depend on ALL variables used in the query function** — a key that omits a
variable serves stale cached data when that variable changes (Pinia Colada).
- **Dynamic query keys need a getter, not a plain value** — pass `key: () => [...]` so the key
re-evaluates reactively; a plain array snapshot never updates.
- **Prefer `refresh()` over `refetch()`** — `refresh()` reuses in-flight requests and respects
`staleTime`; `refetch()` always forces a new fetch.
- **Avoid `index.vue` route files** — use a named group like `pages/(home).vue` for a meaningful
route name.
- **Use explicit route param names** — `userId` not `id`, `postSlug` not `slug`, for type-safe,
self-documenting routes.
- **Watching a destructured prop needs a getter** — `const { count } = defineProps()` stays reactive
in templates, but `watch(count, ...)` watches a snapshot; use `watch(() => count, ...)`
(`references/core-new-apis.md`).
- **`onWatcherCleanup()` must be called synchronously** — invoking it after an `await` throws, since
the active-watcher context is gone; use the `onCleanup` callback param for post-await cancellation
(`references/core-new-apis.md`).
## Anti-Patterns
| Don't | Do Instead |
| ---------------------------------- | -------------------------------- |
| Options API (`data()`, `methods:`) | Composition API `<script setup>` |
| `this.$store` / Vuex | Pinia with setup store syntax |
| Direct store state destructuring | `storeToRefs(useMyStore())` |
| `v-html` with dynamic content | `v-text` or sanitized rendering |
| `reactive()` for primitives | `ref()` for primitives |
| Watchers when computed works | `computed()` for derived state |
| Global event bus | `provide/inject` or Pinia |
| `defineComponent()` wrapper | `<script setup>` directly |
## Workflow Integration
Auto-activates during Build when a Vue project is detected (`.vue` files, `vue` in
`package.json`). Loaded by the `developer` agent alongside `mk:typescript`.Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!