Getting started with Vue 3 + Pinia Store + TypeScript by building a Grocery List App

Getting started with Vue 3 + Pinia Store + TypeScript by building a Grocery List App

Introduction

Let's build a grocery list application using Vue 3 with Typescript and the Vue store Pinia! I just found out that Edward have tweeted about publishing the documentation for Pinia so I thought I'd share how we can create a simple application using this store.

I will only be covering the very basic implementation of the Pinia store.

Pre-Requisites

This article assumes the basic knowledge and understanding or familiarity with:

  • Vue 3 (Composition API)
  • TypeScript
  • Prior understanding of what State Management is

I will be using TypeScript for this application so I hope you understand at least the basic type annotations. Otherwise let's get right at it and start building this app!

Installation

If you don't have the Vue CLI installed yet then make sure to install it, or if your Vue CLI isn't updated yet then make sure it is on the latest version.

$ npm i -g @vue/cli

If you are on a Linux distro then add sudo at the beginning of the command since we are installing Vue CLI globally.

And once that is done let's ask Vue CLI to scaffold a Vue 3 project for us. Make sure you have selected Vue 3.

$ vue create vue-3-pinia-grocery-app

And once that is done navigate into the app and open the project in your IDE.

$ cd vue-3-pinia-grocery-app && code .

Then let us add our sugar, TypeScript.

$ vue add typescript

For now these are my selected options, you can choose on your own if you prefer.

image

Next is to install Pinia as the dependency for this project.

$ npm install pinia@next

And lastly install faker since I am kind of lazy to create forms for this tutorial and creating a form and validating it is sort of an out of scope. So to make things quick, let's just generate some random data from this faker package.

$ npm install faker
$ npm install --save @types/faker

$ npm install uuid
$ npm install --save-dev @types/uuid

Since I plan to use some fake data for quick data generation. Update your model code as I will have a method called generateFakeData() to generate an Item.

import { v4 as uuidv4 } from "uuid";
import * as faker from "faker";

export interface Item {
  id: string;
  name: string;
  description?: string;
  quantity: number;
  createdAt: Date;
  deletedAt?: Date;
}

export function generateFakeData(): Item {
  return {
    id: uuidv4(),
    quantity: Math.random(),
    name: faker.lorem.word(),
    description: faker.lorem.words(),
    createdAt: new Date(),
  };
}

And once that is done let us run our Vue application.

$ npm run serve

Data Model

Since we are building a Grocery List application then we should model our data. The core model to have is an Item.

So to define the model,

export interface Item {
  id: string;
  name: string;
  description?: string;
  quantity: number;
  createdAt: Date;
  deletedAt?: Date;
}

So under the src directory create a models directory and it's where this Item model will reside. So create a file name it as item.model.ts.

Then we'll have the following,

image

Pinia Setup

Open the main.ts file under the src directory and make sure to chain the following method use() and pass in createPinia() as the first parameter.

import { createPinia } from "pinia";
import { createApp } from "vue";
import App from "./App.vue";

createApp(App)
  .use(createPinia())
  .mount("#app");

Next is to create a directory and name it as store and inside it create a file called index.ts

Then to define this main store,

import { generateFakeData, Item } from "@/models/item.model";
import { defineStore } from "pinia";

export type RootState = {
  items: Item[];
};

export const useMainStore = defineStore({
  id: "mainStore",
  state: () =>
    ({
      items: [],
    } as RootState),

  actions: {
    createNewItem(item: Item) {
      if (!item) return;

      this.items.push(item);
    },

    updateItem(id: string, payload: Item) {
      if (!id || !payload) return;

      const index = this.findIndexById(id);

      if (index !== -1) {
        this.items[index] = generateFakeData();
      }
    },

    deleteItem(id: string) {
      const index = this.findIndexById(id);

      if (index === -1) return;

      this.items.splice(index, 1);
    },

    findIndexById(id: string) {
      return this.items.findIndex((item) => item.id === id);
    },
  },
});

We have defined the most basic functionality, creating, updating an deleting an item from our grocery list. And that is more done enough for getting to know how to setup Pinia as your Vue store.

Demo

This is the best looking UI... Yeah.

image

Demo

As you can see from this setup we are able to use the Pinia store, that we are able to add an Item, update it and delete it.

Summary

We learned how to setup Pinia with Vue 3 and TypeScript. What I like Pinia is that it is built with TypeScript already that means the store provides us all the auto-completion that we want and the reason we love about TypeScript. Pinia is also very intuitive which we notice it was very similar to how Vuex is implemented.

But there are more of its features that you can read about from the official documentation

I hope you find this useful, cheers!

Full source code can be found from the repository