All skills
vuejs-ai avatar

/vue-pinia-best-practices

@bc922e4 official
by Vue AIvuejs-ai/skills2.9k stars
167

Pinia stores, state management patterns, store setup, and reactivity with stores.

Use this Skill: https://skilld.dev/gh/vuejs-ai/skills/vue-pinia-best-practices

This session only. Nothing lands on disk.

referencestore-method-binding-parentheses.md

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

Include Parentheses When Calling Store Methods in Templates

Impact: MEDIUM - When calling methods on a reactive store object in Vue templates, you must include parentheses (even without arguments) to ensure the method is called with the correct this context. Without parentheses, this inside the method may not refer to the store.

This is a subtle gotcha that can cause hard-to-debug issues with hand-rolled reactive stores.

Task Checklist

  • Always use parentheses when calling store methods in templates: @click="store.increment()"
  • Avoid passing store methods as bare references: @click="store.increment" is problematic
  • Consider using arrow functions or Pinia (which handles this automatically)
  • Test store method calls work correctly with this references

The Problem

// store.js
import { reactive } from 'vue'

export const store = reactive({
  count: 0,
  increment() {
    this.count++  // 'this' should refer to the store
  }
})
<template>
  <!-- WRONG: Without parentheses, 'this' binding may be lost -->
  <button @click="store.increment">
    {{ store.count }}
  </button>
</template>

When you write @click="store.increment" (without parentheses), Vue passes the method as a callback to the event handler. The method gets called without being bound to the store object, so this inside increment() may be undefined or the global object instead of the store.

The Solution

<template>
  <!-- CORRECT: With parentheses, method is called with correct context -->
  <button @click="store.increment()">
    {{ store.count }}
  </button>
</template>

With parentheses, you're telling Vue to call store.increment() directly, which preserves the this context as the store object.

Why This Happens

In JavaScript, when you reference a method without calling it, you get the function itself without its binding:

const store = {
  count: 0,
  increment() {
    this.count++
  }
}

// With parentheses - correct context
store.increment()  // this === store ✓

// Without parentheses - getting the function
const fn = store.increment
fn()  // this === undefined (in strict mode) ✗

Vue's event handler behavior:

  • @click="store.increment" - Vue receives store.increment as a function and calls it later
  • @click="store.increment()" - Vue evaluates store.increment() when the event fires

Alternative Solutions

1. Use Arrow Functions in the Template

<template>
  <button @click="() => store.increment()">
    {{ store.count }}
  </button>
</template>

2. Bind Methods in the Store Definition

// store.js
import { reactive } from 'vue'

const state = reactive({
  count: 0
})

// Methods defined separately, arrow functions don't have 'this' issues
export const store = {
  get count() { return state.count },
  increment: () => { state.count++ }
}

3. Use Pinia (Recommended)

Pinia handles method binding correctly:

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

export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  actions: {
    increment() {
      this.count++  // Pinia ensures 'this' is correct
    }
  }
})
<script setup>
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
</script>

<template>
  <!-- Both work correctly with Pinia -->
  <button @click="counter.increment">Works</button>
  <button @click="counter.increment()">Also works</button>
</template>

4. Wrap in a Local Function

<script setup>
import { store } from './store'

// Wrap store method to ensure correct binding
function increment() {
  store.increment()
}
</script>

<template>
  <!-- Now safe to use without parentheses -->
  <button @click="increment">
    {{ store.count }}
  </button>
</template>

When This Matters

This gotcha specifically affects:

  • Hand-rolled reactive stores using reactive()
  • Methods that reference this inside them
  • Direct method references in templates without parentheses

It does NOT affect:

  • Pinia stores (methods are auto-bound)
  • Arrow function methods (no this binding)
  • Methods that don't use this
  • Method calls with parentheses

Quick Reference

Pattern Safe? Notes
@click="store.method()" Yes Explicit call preserves context
@click="store.method" No* Context may be lost
@click="() => store.method()" Yes Arrow wrapper preserves context
@click="localMethod" Yes Component methods are auto-bound
Pinia: @click="store.action" Yes Pinia handles binding

*Only problematic if the method uses this

Reference

Source: SKILL.md on GitHub

No alerts16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    This skill provides educational content and best practices for Pinia state management in Vue.js applications. It is a documentation-focused skill with no malicious patterns detected.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer7mo

    7 files scanned · No issues

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at bc922e4. 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 8 months ago
version
1.0.0
author
github.com/vuejs-ai
  • Vue
  • pinia
  • state-management
  • reactivity
  • store-setup
  • devtools
  • ssr

README badge

README badge for vuejs-ai/skills/vue-pinia-best-practices

Provides Pinia store setup patterns, reactivity gotchas, and state management conventions for Vue apps. Covers destructuring pitfalls, DevTools integration, SSR considerations, and filter persistence strategies.

Generated from the current SKILL.md.

Does this skill cover setup stores and options stores?
Yes. The skill addresses both patterns, including common issues like setup stores missing state in DevTools or SSR, and provides guidance on when to use each.
What reactivity issues does this skill handle?
It covers store destructuring breaking reactivity, method binding losing context in templates, and how to maintain reactive updates when accessing store properties.
Does this skill address SSR or DevTools integration?
Yes. It includes troubleshooting for setup stores not exposing state correctly in DevTools and SSR environments.
Does this cover ephemeral state like filters or URL sync?
Yes. The skill includes patterns for handling filters that reset on refresh and guidance on syncing state with URLs for shareable application state.

Generated from the current SKILL.md. These answers refresh after source changes.