mustafa@dev:~$cat ~/post/nuxtjs-pinia-your-apps-data-flow-dream-date.md
[tech]

NuxtJS & Pinia — your app's data flow dream date

// 2023-12-245m read

Nuxt & Pinia

Nuxt is a meta-framework built on top of Vue that offers a variety of rendering modes and a ton of developer conveniences including auto-imported components and composables, page-based routing, and more.

Pinia, on the other hand, is a store library and state management framework for Vue.js. Designed primarily for building front-end web applications, it uses declarative syntax and offers its own state management API. It was recently declared by the Vue core team as the official recommendation for state management in the Vue ecosystem.

nuxt pinia

Getting started with Pinia in NuxtJS

This guide walks you through setting up a data store, saving data in state, and then using it in a reactive way within a NuxtJS application.

Prerequisites

Make sure you have a Nuxt 3 application set up. If you don't already have one, consider using the Nuxtbase Starter template as a starting point.

Also, install the necessary package: @pinia/nuxt.

Setting up Pinia

Now that we have our prerequisites in place, let's dive into setting up Pinia:

store/todos.ts// shiki
// store/todos.ts

import { defineStore } from 'pinia'

export const useTodos = defineStore('todos', {
  state: () => ({
    todos: null as unknown,
    error: null as unknown,
    loading: false
  }),

  getters: {},

  actions: {
    async fetchTodos() {
      this.loading = true

      try {
        const { data, pending, error } = await useFetch(
          'https://jsonplaceholder.typicode.com/todos'
        )

        if (pending.value) {
          console.log('Fetching data...')
        } else if (error.value) {
          console.error('Error fetching data:', error.value)
          this.error = error.value
        } else if (data.value) {
          this.todos = data.value
          console.log('Data fetched successfully:', this.todos)
        }
      } catch (error) {
        console.error('Error in fetchData:', error)
        this.error = error
      } finally {
        this.loading = false
      }
    }
  }
})
  • Each store should have a unique name.
  • Initialize the state.
  • Use getters to access part of the state or access it through a parameter.
  • Change the state with actions. Access the state with this.
  • useFetch is a composable provided by Nuxt 3. It offers a reactive way to make HTTP requests. It can be called directly in a setup function, plugin, or route middleware.

Using the store in a component

TodosList.vue// shiki
<template>
  <div>
    <button @click="fetchTodos">Fetch Todos</button>
    <div v-if="loading">Loading...</div>
    <div v-if="error">An error occurred: {{ error }}</div>
    <div v-if="todos">{{ todos }}</div>
  </div>
</template>

<script setup lang="ts">
  import { useTodos } from "@/stores/todos";
  import { storeToRefs } from "pinia";

  const todosStore = useTodos();

  const { loading, error, todos } = storeToRefs(todosStore);

  const fetchTodos = async () => {
    await todosStore.fetchTodos();
  };
</script>

I used the store in a Composition API setup component, which is the modern way of building components in Vue 3.

By setting up a store instance, you can access the state directly:

html// shiki
<template>
  <div v-if="todos">{{ todosStore.todos }}</div>
</template>

This displays the todos state from the todosStore. It's handy for fetching data when your app loads. However, for scenarios requiring reactive state like:

  • Dynamic data display — automatically updating a part of your UI displaying the counter value whenever the counter changes.
  • Conditional rendering — parts of your UI that should only be displayed under certain conditions.
  • Form handling — making form inputs reactive, so they automatically update your app's state as users type.
  • List rendering — if you have a list of items in state, any changes to the list (like adding or removing items) will automatically update the UI.

This is where storeToRefs() comes in. It takes all the parts of the store's state and makes them reactive pieces you can use throughout your application.

Conclusion

NuxtJS and Pinia offer a powerful combination for building scalable, maintainable, and efficient Vue.js applications. Their integration simplifies state management, improves code organization, and enhances developer experience.