Thumbnail image

NuxtJS & Pinia - Your App's Data Flow Dream Date🍍

24/12/2023 4-minute read

Nuxt & Pinia are:

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 and recently declared by the Vue core team as the official recommendation for state management in the Vue ecosystem.

Getting Started with Pinia in NuxtJS:

This guide will walk you through the process of setting up a data store, saving data in state, and then using it in a reactive way within a NuxtJS application.


Before we begin, ensure that you have a Nuxt 3 application set up. If you don’t already have one, consider using Nuxtbase Starter template as a starting point.

Also, make sure you have installed the necessary package here which is: @pinia/nuxt.

Setting Up Pinia

Now that we have our prerequisites in place, let’s dive into setting up Pinia in our NuxtJS application

// 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(

        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
  • Store should have a unique name.
  • Initialize the state.
  • Use getters to access part of the state or access the state through a parameter.
  • Change the state with actions. Access the state with this.
  • useFetch is a composable provided by Nuxt3. It offers a reactive way to make HTTP requests. It can be called directly in a setup function, plugin, or route middleware.

Use the store in a component

    <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>

<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();
  • I used the store in Composition API setup component, which is the new way of building components in Vue 3.0. (There’s another way called the Options API, more details).

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

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

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

    • Dynamic Data Display: Automatically updating a part of your UI displaying the counter value whenever the counter changes.

    • Conditional Rendering: You might have 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 your state and because list is reactive, any changes to the list (like adding or removing items) will automatically update the UI.

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


In summary, 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.