Pinia Actions and Async State

Pinia Actions and Async State

Pinia actions can be asynchronous, making it easy to fetch data from APIs and manage loading and error states in a centralised store.

Store with Async Action

// src/stores/users.js
import { defineStore } from 'pinia'
import { ref } from 'vue'

export const useUsersStore = defineStore('users', () => {
  const users   = ref([])
  const loading = ref(false)
  const error   = ref(null)

  async function fetchUsers() {
    loading.value = true
    error.value   = null
    try {
      const response = await fetch('/api/users')
      if (!response.ok) throw new Error('Failed to load users')
      users.value = await response.json()
    } catch (e) {
      error.value = e.message
    } finally {
      loading.value = false
    }
  }

  async function createUser(data) {
    const response = await fetch('/api/users', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data),
    })
    const newUser = await response.json()
    users.value.push(newUser)
    return newUser
  }

  return { users, loading, error, fetchUsers, createUser }
})

Component Usage

<script setup>
import { onMounted } from 'vue'
import { useUsersStore } from '@/stores/users'

const store = useUsersStore()
onMounted(() => store.fetchUsers())
</script>

<template>
  <div v-if="store.loading">Loading...</div>
  <div v-else-if="store.error">{{ store.error }}</div>
  <ul v-else>
    <li v-for="user in store.users" :key="user.id">{{ user.name }}</li>
  </ul>
</template>
VueJS {"id":61,"topic_id":32,"name":"State Management with Pinia","slug":"state-management-with-pinia","image":null,"description":"<p>Manage shared application state cleanly using Pinia, the official Vue state management library.<\/p>","icon":null,"class":null,"color":null,"status":0,"order":6,"created_at":"2026-05-04T20:50:50.000000Z","updated_at":"2026-05-04T20:50:50.000000Z"} - List of Contents

Related Tutorials: