All skills
hyf0 avatar

/vue-pinia-best-practices

@bc922e4 official
by hyf0hyf0/vue-skills2.9k stars
167

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

Use this Skill: https://skilld.dev/gh/hyf0/vue-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

README badge

README badge for hyf0/vue-skills/vue-pinia-best-practices

Teaches Pinia store setup, reactivity patterns, and state management conventions for Vue applications. Covers common mistakes like destructuring breaking reactivity, setup stores missing state in DevTools, and losing method context in templates.

Generated from the current SKILL.md.

What does this skill cover?
Pinia store setup, state management patterns, reactivity gotchas, and common errors like getActivePinia and broken destructuring in Vue components.
Does this skill address SSR with Pinia?
Yes. It includes guidance on setup stores missing state in DevTools or SSR environments.
How does this handle filter state across page refreshes?
The skill references patterns for persisting ephemeral filters using URL state rather than relying on store alone.
Does this cover store method binding issues?
Yes. It addresses the gotcha where store methods lose context when called directly in templates and explains the parentheses solution.

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