NuxtJS & Pinia — your app's data flow dream date
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.
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
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
gettersto access part of the state or access it through a parameter. - Change the state with
actions. Access the state withthis. useFetchis 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
<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:
<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.