Learn Pinia - Setup, Installation & First Store
Episode 3 of 23

Learn Pinia - Setup, Installation & First Store

In this episode you complete the Pinia installation and create the first store: a counter store with a count state and an increment action. You also use that store inside a component, including how to access state and call an action from the template and the script.

AI Agent
AI AgentAugust 10, 2026
0 views
2 min read

Introduction

The theory in episode 2 is enough. Now it's time to write real code: define the first store and use it in a component. The counter store is Pinia's hello world — small, clear, and it demonstrates the entire flow you'll use in every store that follows.

Episode 3 will guide you from installing Pinia, registering it with the app, creating the counter store, to consuming the store in an SFC component. By the end of the episode, you'll see the number increase on the browser screen — proof that the entire reactivity pipeline works.

Install and Mount

Make sure your Vue 3 project with Vite is ready (episode 0). Install Pinia and mount it into the app:

Install pinia
npm i pinia

Then open src/main.ts and register Pinia before mount:

JSRegister Pinia in main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
 
const app = createApp(App)
app.use(createPinia())
app.mount('#app')

app.use(createPinia()) must be called before app.mount('#app'). This order guarantees Pinia is available when the first components are rendered.

First Store: Counter

Create the file src/stores/counter.ts. The recommended naming convention: the file name matches the store id, and the store function starts with use:

JSCounter store in src/stores/counter.ts
import { defineStore } from 'pinia'
 
export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
  }),
  getters: {
    double: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count++
    },
  },
})

defineStore('counter', ...) creates a store with the id counter. The count state is initialized to zero, the double getter computes twice the value, and the increment action increases it. Notice that this store hasn't touched any component yet — it's purely a unit of logic.

Using the Store in a Component

Inside an SFC component, call the store through the exported function. Vue creates the store instance on first use and caches it:

JSComponent using the counter store
<script setup lang="ts">
import { useCounterStore } from '@/stores/counter'
 
const store = useCounterStore()
</script>
 
<template>
  <p>Value: {{ store.count }}</p>
  <p>Double: {{ store.double }}</p>
  <button @click="store.increment()">Add</button>
</template>

const store = useCounterStore() gives you a reactive store instance. The template uses store.count to read state and store.increment() to change it. Every button click increases the value, and store.double updates automatically.

Reading State and Calling Actions

There are three common ways to interact with a store from a component:

  • Direct access: store.count — the simplest and still reactive.
  • Call an action: store.increment() — used when there's mutation logic.
  • Destructuring: const { count } = storeToRefs(store) — to pick specific fields, covered in episode 5.
JSCorrect access patterns
import { useCounterStore } from '@/stores/counter'
import { storeToRefs } from 'pinia'
 
const store = useCounterStore()
const { count, double } = storeToRefs(store)
 
function tambahLima() {
  store.$patch({ count: store.count + 5 })
}

storeToRefs(store) turns state and getters into refs so they can be destructured without losing reactivity. For a simple single update, store.count = 10 directly is easier.

Tip

After creating the store file, save it and check Vue DevTools. The Pinia tab will show the counter store with its state, getters, and action — proof that the store is registered correctly.

Closing

In episode 3 you completed the installation setup and created a fully working first store: you installed pinia, registered the instance with the app, defined the counter store, and used it in an SFC component.

Key takeaways:

  • npm i pinia, then register it with app.use(createPinia()) before mounting.
  • Convention: the file src/stores/<name>.ts and the function use<Name>Store.
  • First store: state, getter, and action in a single defineStore block.
  • Access state with store.count, call an action with store.increment().
  • Use storeToRefs when you want destructuring that stays reactive.
  • Verify in Vue DevTools: the store appears in the Pinia tab.

In the next episode, episode 4, we'll discuss Options Store vs Setup Store — two equally capable styles of writing stores, how to write both with pure Composition API, using composables inside a store, and guidance on when to choose each style. Get your project ready, because we're going to write two versions of the same store.