A hands-on tutorial for using Contentful with Vue

stahlwalker

Luke Stahl

Posted on August 18, 2022

A hands-on tutorial for using Contentful with Vue

NOTE: This article originally appeared on the Contentful Blog

An advantage of using a headless CMS is that it allows you to have a single data source. You can use Contentful’s REST APIs to serve content to multiple devices, such as web, mobile, and IoT devices, or different tech stacks on the client side.

Contentful is an integrated, API-first content platform that enables you to create and deliver engaging digital experiences quickly and efficiently.

This article demonstrates how to set up a project on Contentful, create a space, add data, and publish this data. Then, it goes through how to pull the data and display it on your web app using Vue.

Vue is a frontend framework that allows you to implement web applications while improving scalability quickly. It abstracts low-level functionalities, letting you manage and display data properly. It also allows you to reuse code using components, meaning you can reuse a user interface without repeating code.

In this demo, you’ll use the Content Delivery API to get ecommerce data and display it within a reusable Vue component. Specifically, you’ll build an ecommerce store that allows you to select and display bags and add them to a cart. You can view the complete project code on GitHub.

Purse shopping cart gif

Getting started

To get started, create your free Contentful account.

When you sign up, you’ll automatically get your first space. You can think of a space as a project within Contentful. For this tutorial, you can either rename the space that was created automatically or create a new one.

Next, create a content model and content type. Consult the beginner’s guide to Contentful for step-by-step instructions.

Image description

Your content type needs a name that describes your collective data entries. Since this demo is an ecommerce store for bags, name the content type “Bags.”

Image description

Next, create fields in your content model. For your bag store, you need three fields: Title, ImageUrl, and Description. Be sure that the imageUrl points to a valid image or else the display will not work properly.

Click + AddField and add a new field.

Image description

Now, click the Content tab and add some example entries using these fields. Make sure to publish each entry once you add it.

For example, below is a listing for a brown messenger handbag, including a title, description, and URL.

Image description

After a few entries have been added and published, you’ll be ready to pull your data from a RESTful API.

Image description

Using Contentful’s Content Delivery API

Contentful makes it possible to access your data using the Content Delivery API. This REST API allows you to access the data in your space through a single request. Contentful provides a GraphQL endpoint, but this demo will focus on the REST API.

Now that you’ve added all your data to the dashboard, you can pull the data into your Vue application using the Content Delivery API. To proceed, make sure you have the following:

  • Space ID — This is a unique ID associated with each space.

  • Content Delivery API access token — This is a token used when interacting with the Content Delivery API.

You can find both by clicking Settings in the top-right of the dashboard and selecting API Keys.

Image description

Add the following endpoint on Postman, and it will return all our field entries:

https://cdn.contentful.com/spaces/{SPACE_ID}/environments/master/entries?access_token={ACCESS_TOKEN}

Image description

Your data is now returned from Contentful’s endpoint. Your next task is setting up your Vue code and pulling the data.

Installing Vue CLI

To initiate your Vue app, you need to install Vue CLI globally on your machine. If you already have it installed, you can skip this step.

npm install -g @vue/cli

Once it’s installed, you’ll use Vue CLI to initiate your project. Initiate a new project using this command in your terminal:

vue create vue-ecommerce-site

Within your terminal, you’ll have to manually select your preferred options for your project until the appropriate Vue boilerplate code is ready. Please select Vue 2, as that is what this tutorial is based upon. We suggest you deselect both Babel and Linter, as they can cause complications. Proceed with the installation. Once the installation is completed, you can navigate to your project’s folder:

cd vue-ecommerce-site

Now, let’s install vue-router. Please install the following version:

npm i vue-router@3.5.4

Also, install axios. You’ll use this popular HTTP client to make your API request to Contentful:

npm i axios

Then, start the server:

npm run serve

It should fire up your project, and you should see this in the browser:

Image description

Go into the components folder and create two new components called ContentfulComponent.vue and CartComponent.vue.

ContentfulComponent.vue displays the list of bags, while CartComponent.vue will display our cart.

Now, let’s make use of our vue-router. Create a router folder inside our src and within the router, we can now add index.js.

import VueRouter from 'vue-router'
import ContentfulComponent from '../components/ContentfulComponent.vue'
import CartComponent from '../components/CartComponent.vue';
import Vue from "vue";

Vue.use(VueRouter);
const routes = [
  {
    path: '/',
    name: 'ContentfulComponent',
    component: ContentfulComponent
  },
  {
    path: '/cart',
    name: 'CartComponent',
    component: CartComponent
  }
]
const router = new VueRouter({
  routes,
  mode: 'history'
})
export default router
Enter fullscreen mode Exit fullscreen mode

Now, go into your main.js and import the router.

import Vue from "vue"
import App from "./App.vue"
import router from "./router/index"

Vue.config.productionTip = false

new Vue({
  router,
render: h => h(App)
}).$mount("#app");
Enter fullscreen mode Exit fullscreen mode

Then, go into your App.vue file, which is automatically created in the boilerplate, and import the component for use. Do not remove the styling.

<template>
  <div id="app">
    <h3>Bags Store</h3>
    <img
      alt="E-commerce logo"
      src="https://photo-cdn2.icons8.com/cfD33FbEpD4Srs0gXnuRWXFlet6KXB0dnU-YHbZTVzU/rs:fit:715:1072/czM6Ly9pY29uczgu/bW9vc2UtcHJvZC5h/c3NldHMvYXNzZXRz/L3NhdGEvb3JpZ2lu/YWwvMS9lY2JjNzFj/YS00NjU2LTRjNWIt/OTM1MC0wMTBlMTI4/N2I4ODYuanBn.jpg"
      width="25%"
    />
    <router-view></router-view>
  </div>
</template>
<script>
export default {
  name: "App",
  components: {},
};
</script>
Enter fullscreen mode Exit fullscreen mode

Now, go into the ContentfulComponent.vue file and implement your data. First, declare the theBags array under data:

data() {
  return {
    theBags: []
  }
}
Enter fullscreen mode Exit fullscreen mode

Next, add a new function called getBags under methods:

methods: {
    getBags() {
      axios
        .get(
          "https://cdn.contentful.com/spaces/{SPACE_ID}/environments/master/entries?access_token={ACCESS_TOKEN}"
        )
        .then((res) => {
          console.log(res.data.items);
          this.theBags = res.data.items;
        });
    },
  },
Enter fullscreen mode Exit fullscreen mode

This pulls your data and assigns it to the theBags array you’ve declared earlier. You need to import axios using the following command:

import axios from "axios";

Finally, to fully implement your business logic, you’ll add a mounted lifecycle that executes on load. Add this.getBags(); so it can trigger the function once the page loads.

mounted() {
    this.getBags();
},
Enter fullscreen mode Exit fullscreen mode

Within the , you’ll loop over the theBags array and display its elements.

<template> 
  <div> 
    <div v-if="theBags"> 
      <div :key="bag.fields.title" v-for="bag in theBags"> 
        <div class="indi__item"> 
          <img class="indi__img" :src="bag.fields.imageUrl" /> 
          <h3>{{ bag.fields.title }}</h3> 
          <h3>Description: {{ bag.fields.description }}</h3> 
        </div> 
      </div> 
    </div> 
  </div> 
</template> 
Enter fullscreen mode Exit fullscreen mode

Find the full code for the component below. Be sure to add your own space ID and Content Delivery API access token.

<template> 
  <div> 
    <router-link to="/cart"><h3>Open Cart</h3></router-link>
    <div v-if="theBags"> 
      <div :key="bag.fields.title" v-for="bag in theBags"> 
        <div class="indi__item"> 
          <img class="indi__img" :src="bag.fields.imageUrl" /> 
          <h3>{{ bag.fields.title }}</h3> 
          <h3>Description: {{ bag.fields.description }}</h3> 
        </div> 
      </div> 
    </div> 
  </div> 
</template> 
<script> 
import axios from "axios"; 

export default { 
  name: "ContentfulComponent", 
  data() { 
    return { 
      theBags: [], 
    }; 
  }, 
  methods: { 
    getBags() { 
      axios 
        .get( 
          "https://cdn.contentful.com/spaces/{SPACE_ID}/environments/master/entries?access_token={ACCESS_TOKEN}" 
        ) 
        .then((res) => { 
          console.log(res.data.items); 
          this.theBags = res.data.items; 
        }); 
    }, 
  }, 
  mounted() { 
    this.getBags(); 
  }, 
}; 
</script>
Enter fullscreen mode Exit fullscreen mode

You can also add the following styling:

<style>
.indi__item {
  border: 1px solid black;
  margin-bottom: 2rem;
  padding: 0.5rem;
  display: grid;
  justify-items: center;
}
.indi__img {
  height: 16rem;
  margin-bottom: 1rem;
}
</style>
Enter fullscreen mode Exit fullscreen mode

And here’s what this code looks like in action:

Image description

Letting shoppers add items to their cart

Next, you’ll create a cart feature for your online bag store. This lets your shoppers select the bags they need and add them to their cart.

Return to your ContentfulComponent.vue file and add a new method under methods called addToCart.

Add the code below to this method. With this code, you create a cart item in local storage, allowing your application to store the specific item you want to save to the cart. Then, to enhance user experience, you add an alert to notify users when they have successfully added an item to the cart.

addToCart(item) { 
      if (!localStorage.getItem("cart")) { 
        localStorage.setItem("cart", JSON.stringify([])); 
      } 
      //console.log(item); 
      let cartItems = []; 
      cartItems = JSON.parse(localStorage.getItem("cart")) || []; 

      localStorage.getItem("cart"); 
      cartItems.push(item); 
      console.log(cartItems); 

      localStorage.setItem("cart", JSON.stringify(cartItems)); 
      alert("added to cart"); 
}, 
Enter fullscreen mode Exit fullscreen mode

You also need to add a button to trigger this addToCart function. You’ll add this to the "indiitem" class under :

<div class="indi__item"> 
  <img class="indi__img" :src="bag.fields.imageUrl" /> 
  <h3>{{ bag.fields.title }}</h3> 
  <h3>Description: {{ bag.fields.description }}</h3> 
  <button @click="addToCart(bag.fields)">Add to cart</button> 
</div>
Enter fullscreen mode Exit fullscreen mode

Here’s what your demo now looks like:

Image description

Creating the cart component

Now that you’ve added the option to add an item to the cart, you need to give shoppers access to view their carts. So, it’s time to create a cart component that displays all the items in a shopper’s cart.

This component will loop through the cart’s local storage and display the cart list. Plus, users can remove individual items they don’t want from their cart.

To build the cart component, add a script tag, where you’ll add an empty cart array into data.

 data() {
    return {
      cart: [],
    };
  },
Enter fullscreen mode Exit fullscreen mode

Next, add a method that gets your cart from the local storage and assigns it to the cart array above:

methods: { 
    getCart() { 
      if (!localStorage.getItem("cart")) { 
        localStorage.setItem("cart", JSON.stringify([])); 
      } 
      this.cart = JSON.parse(localStorage.getItem("cart")); 
    }, 
  }, 
Enter fullscreen mode Exit fullscreen mode

Then, add a beforeMount lifecycle to trigger the getCart method on load.

beforeMount() { 
    this.getCart(); 
  },
Enter fullscreen mode Exit fullscreen mode

The final method you need to add is the removeFromCart method, which allows you to remove a particular item from the cart array in the local storage. It receives the imageUrl from the item and uses findIndex() to return the index of that specific item in the array. Then, it uses that index to remove the item from the array using splice() and saves the updated array in the local storage cart item.

methods: { 
removeFromCart(itemImage) { 
    const cartItems = JSON.parse(localStorage.getItem("cart")); 
    const index = cartItems.findIndex(({ imageUrl }) => imageUrl === itemImage); 
    cartItems.splice(index, 1); 
    localStorage.setItem("cart", JSON.stringify(cartItems)); 
    this.cart = JSON.parse(localStorage.getItem("cart")); 
   }, 
}
Enter fullscreen mode Exit fullscreen mode

Your template now looks like this:

<template> 
  <div> 
    <h1>Cart</h1> 
    <div v-for="item of cart" :key="item.id"> 
      <div class="indi__item"> 
        <p>{{ item.name }}</p> 
        <img class="indi__img" :src="item.imageUrl" /> 
        <button @click="removeFromCart(item.imageUrl)">remove from cart</button> 
      </div> 
    </div> 
    <router-link to="/"><h3>Back to store</h3></router-link> 
  </div> 
</template>
Enter fullscreen mode Exit fullscreen mode

This code loops through the cart array and displays individual items. For each item, there is a button that can be used to remove the item by firing removeFromCart.

Here’s the full component code:

<template> 
  <div> 
    <h1>Cart</h1> 
    <div v-for="item of cart" :key="item.id"> 
      <div class="indi__item"> 
        <p>{{ item.name }}</p> 
        <img class="indi__img" :src="item.imageUrl" /> 
        <button @click="removeFromCart(item.imageUrl)">Remove from cart</button> 
      </div> 
    </div> 
    <router-link to="/"><h3>Back to store</h3></router-link> 
  </div> 
</template> 

<script> 
export default { 
  name: "CartComponent", 
  data() { 
    return { 
      cart: [], 
    }; 
  }, 
  methods: { 
    removeFromCart(itemId) { 
      const cartItems = JSON.parse(localStorage.getItem("cart")); 
      const index = cartItems.findIndex(({ imageUrl }) => imageUrl === itemId); 
      cartItems.splice(index, 1); 
      localStorage.setItem("cart", JSON.stringify(cartItems)); 
      this.cart = JSON.parse(localStorage.getItem("cart")); 
    }, 
    getCart() { 
      if (!localStorage.getItem("cart")) { 
        localStorage.setItem("cart", JSON.stringify([])); 
      } 
      this.cart = JSON.parse(localStorage.getItem("cart")); 
    }, 
  }, 
  beforeMount() { 
    this.getCart(); 
  }, 
}; 
</script>
Enter fullscreen mode Exit fullscreen mode

And, here’s your cart:

Image description

Wrapping up

Using a bag ecommerce store as an example, this tutorial explored how to add data to Contentful and display it in a Vue application. You can find all the relevant code in this repo on GitHub.

A key advantage of building an Vue app using Contentful is that you’ll be working alongside an active community of avid users. So, if you come across any challenges, you can join the Contentful Community to seek help from fellow developers.

To expand on everything you’ve learned in this tutorial, start building with Contentful for free today — no credit card required.

💖 💪 🙅 🚩
stahlwalker
Luke Stahl

Posted on August 18, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related