✨ Strapi MCP is now Generally Available - let your agents manage your Strapi content ✨

App4 min read

E-commerce website with Nuxt.js, GraphQL, Strapi and Stripe (4/7)

March 29, 2022Updated on May 22, 2026

This tutorial is part of the « E-commerce website with Strapi, Nuxt.js, GraphQL and Stripe

Note: The source code is available on Github.

Introduction

All of these dishes look so tasty! What if you could add some of them in a shopping cart?

Deliveroo - Dishes page

Let’s create a new Vuex store to manage the shopping cart.

Step 1 - Setup

Vuex is a state management pattern + library for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion. It also integrates with Vue's official devtools extension to provide advanced features such as zero-config, time-travel debugging, and state snapshot export / import. Learn more about Vuex at What is Vuex?.

Create a new store named cart.js:

    // Folder structure
    deliveroo-clone-tutorial
    └── frontend
        └── store
            ├── cart.js // new store
            ├── index.js
            └── README.md

Then, copy/paste the following code:

    // store/cart.js
    import Cookies from 'js-cookie'
    
    export const state = () => ({
      items: [],
    })
    
    export const mutations = {
      setItems(state, items) {
        state.items = items
      },
      add(state, item) {
        const record = state.items.find((i) => i.id === item.id)
    
        if (!record) {
          state.items.push({
            quantity: 1,
            ...item,
          })
        } else {
          record.quantity++
        }
    
        Cookies.set('cart', JSON.stringify(state.items))
      },
      remove(state, item) {
        const record = state.items.find((i) => i.id === item.id)
    
        if (record.quantity > 1) {
          record.quantity--
        } else {
          const index = state.items.findIndex((i) => i.id === item.id)
          state.items.splice(index, 1)
        }
    
        Cookies.set('cart', JSON.stringify(state.items))
      },
      emptyList(state) {
        state.items = []
        Cookies.set('cart', JSON.stringify(state.items))
      },
    }
    
    export const getters = {
      items: (state) => {
        return state.items
      },
      price: (state) => {
        return state.items.reduce(
          (accumulator, item) =>
            accumulator + item.attributes.price * item.quantity,
          0
        )
      },
      numberOfItems: (state) => {
        return state.items.reduce(
          (accumulator, item) => accumulator + item.quantity,
          0
        )
      },
    }

If you are new to Vuex, the terms state, mutations, and actions might be confusing. However, the concept behind each is quite simple. We create a state object that holds the initial state an empty list of items. Next we create an object mutations for storing our add, remove, empty events. Finally, we create an object actions to store functions that dispatch mutations.

For this tutorial, you will store cart data in browser cookies. Let’s install some libraries for handling browser cookies:

# Ctrl + C to close process
yarn add js-cookie cookieparser

Now you want to add the cart to your pages. To do so you are going to create a Cart component that will be used in our Header.vue component.

Step 2 - Create a new Cart component

Let’s create a new Cart component to display user stored dishes. Create a new Cart.vue file inside the components directory.

    // components/Cart.vue
    <template>
      <div>
        <div v-if="price > 0">
          <div
            v-for="dish in selectedDishes"
            :key="dish.id"
            class="uk-grid-small uk-flex"
            uk-grid
          >
            <div class="uk-width-expand">
              <p class="uk-margin-remove-bottom">{{ dish.attributes.name }}</p>
              <p class="uk-text-meta uk-margin-remove-top">
                {{ dish.quantity }} &times; {{ dish.attributes.price }}&euro;
              </p>
            </div>
            <div v-if="checkout" class="uk-width-auto">
              <button type="button" uk-close @click="removeFromCart(dish)"></button>
            </div>
          </div>
          <div class="uk-grid-small uk-flex" uk-grid>
            <div class="uk-width-expand">Subtotal</div>
            <div>{{ price }}&euro;</div>
          </div>
          <div v-if="checkout">
            <NuxtLink
              class="uk-button uk-button-secondary uk-width-1-1"
              to="/checkout"
            >
              Checkout
            </NuxtLink>
          </div>
        </div>
        <div v-else class="uk-text-meta">Empty</div>
      </div>
    </template>
    <script>
    import { mapGetters, mapMutations } from 'vuex'
    export default {
      props: {
        checkout: {
          type: Boolean,
          default: true,
        },
      },
      computed: {
        ...mapGetters({
          selectedDishes: 'cart/items',
          price: 'cart/price',
        }),
      },
      methods: {
        ...mapMutations({
          addToCart: 'cart/add',
          removeFromCart: 'cart/remove',
        }),
      },
    }
    </script>

Card component will receive the dishes selected by the user, v-for attribute will loop through the selected dishes and list them as cards in the dropdown. removeFromCart method will be in charge of removing a dish from the shopping cart.

The mapGetters helper simply maps store getters to local computed properties. The mapMutations helper maps component methods to store.commit calls.

Next, let’s update our pages/restaurants/_id.vue, and copy/paste the following code in it.

    // pages/restaurants/_id.vue
    <template>
      <div class="uk-container uk-container-xsmall">
        <span class="uk-heading-small">
          // Link to go back to the previous page
          <NuxtLink class="uk-button uk-button-text" to="/">
            <span uk-icon="arrow-left"></span> go back
          </NuxtLink>
        </span>
    
        // Displaying dishes
        <div v-for="dish in dishes" :key="dish.id">
          <div class="uk-card uk-card-default uk-child-width-1-2 uk-margin" uk-grid>
            <div class="uk-card-body uk-card-small">
              <h2 class="uk-card-title">{{ dish.attributes.name }}</h2>
              <p>{{ restaurant.data.attributes.name }}</p>
              <p>{{ dish.attributes.price }} €</p>
              <button class="uk-button uk-button-primary uk-margin-xlarge-top" @click="addToCart(dish)">
                Add to cart
              </button>
            </div>
            <figure class="uk-card-media-right uk-cover-container">
              <img
                :src="getStrapiMedia(dish.attributes.image.data.attributes.url)"
                :alt="dish.attributes.image.data.attributes.alternativeText"
              />
            </figure>
          </div>
        </div>
      </div>
    </template>
    
    <script>
    import { mapMutations } from 'vuex'
    import { getStrapiMedia } from '@/utils/media'
    import restaurantQuery from '@/apollo/queries/restaurant'
    export default {
      data() {
        return {
          restaurant: Object,
        }
      },
      apollo: {
        restaurant: {
          prefetch: true,
          query: restaurantQuery,
          variables() {
            return { id: this.$route.params.id }
          },
        },
      },
      computed: {
        dishes() {
          if (!this.restaurant?.data) return []
          return this.restaurant.data.attributes.dishes.data
        },
      },
      methods: {
        getStrapiMedia,
        ...mapMutations({
          addToCart: 'cart/add',
          removeFromCart: 'cart/remove',
        }),
      },
    }
    </script>

We called a new mapMutations, addToCart to receive the dish as an argument and perfrom a change in the Vuex store. It adds the selected dish to the card component automatically.

Conclusion

As you may see, you created a full featured shopping card component. In fact you want to reuse this component in two pages: pages/r``estaurants/index.vue and pages/o``rders/checkout.vue that you'll create soon.
🔐 In the next section, you will learn how to authenticate users in your app.

Pierre BurgyChief Executive Officer

Pierre created Strapi with Aurélien and Jim back in 2015. He's a strong believer in open-source, remote and people-first organizations. You can also find him regularly windsurfing or mountain-biking!

App·26 min read

Build a To-Do App with Strapi GraphQL Plugin and Flutter

In this tutorial, we will set up a GraphQL endpoint in a Strapi backend along with Flutter, a powerful opensource UI...

·August 30, 2023
App·16 min read

How to build a To-do App using Next.js and Strapi

How to build a Todo App using Next.js and Strapi Updated July 2023 In this article, we will learn how to use , and to...

·August 29, 2023
How to setup Amazon S3 Upload Provider Plugin for your Strapi App
AppBeginner·14 min read

How to Set up Amazon S3 Upload Provider Plugin for Your Strapi App

Learn how to set up the Amazon S3 Upload Provider Plugin for your Strapi App to work with an Amazon S3 storage bucket.

·July 3, 2023