A hands-on tutorial for using Contentful with Vue
Tags: javascript, cms, tutorial, vue
Published: 2022-08-18 15:43:00

**NOTE**: This article originally appeared on the [Contentful Blog](https://www.contentful.com/blog/2022/08/18/vue-tutorial/)

An advantage of using a [headless CMS](https://www.contentful.com/r/knowledgebase/what-is-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](https://www.contentful.com/developers/docs/references/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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a0b0ngpuiqjme2gxnlxt.gif)

## Getting started 
To get started, [create your free Contentful account](https://www.contentful.com/sign-up/). 

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](https://www.contentful.com/help/contentful-101/#step-3-create-the-content-model) for step-by-step instructions.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5fdwgl8wkg05g4s95st7.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vae21jlwnm108l40vllo.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pgtxkvmiiqanbquhwwul.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bi9mvrfbacoq4us80k11.png)

After a few entries have been added and published, you’ll be ready to pull your data from a [RESTful API](https://www.contentful.com/blog/2021/10/04/what-is-a-rest-api/). 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0vzplhf439ndhptmv6hy.png)

## Using Contentful’s Content Delivery API
Contentful makes it possible to access your data using the [Content Delivery API](https://www.contentful.com/developers/docs/references/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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rgbf4cmiepzqnv5b0i0l.png)

Add the following endpoint on [Postman](https://www.postman.com/), and it will return all our field entries:

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

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aqozbqi85nc7e26armmz.png)

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](https://cli.vuejs.org/) 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](https://www.npmjs.com/package/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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/evpqbm689tvctr9g2es8.png)

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

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

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

```javascript
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");
```

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. 

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

Now, go into the _ContentfulComponent.vue_ file and implement your data. First, declare the _theBags_ array under data:

```javascript
data() {
  return {
    theBags: []
  }
}
```

Next, add a new function called _getBags_ under _methods_:

```javascript
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;
        });
    },
  },
```

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.

```javascript
mounted() {
    this.getBags();
},
```

Within the _<template>_, you’ll loop over the theBags array and display its elements. 

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

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

```javascript
<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>
```
You can also add the following styling:

```javascript
<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>
```
And here’s what this code looks like in action:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1pf6132n1t7fgf4s25hc.gif)

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

```javascript
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"); 
}, 
```

You also need to add a button to trigger this _addToCart_ function. You’ll add this to the _"indi__item"_ class under _<template>_:

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

Here’s what your demo now looks like:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rd5h0irgf8s2zp4dcjvp.gif)

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

```javascript
 data() {
    return {
      cart: [],
    };
  },
```
Next, add a _method_ that gets your cart from the local storage and assigns it to the cart array above:

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

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

```javascript
beforeMount() { 
    this.getCart(); 
  },
```

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.  

```javascript
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")); 
   }, 
}
```

Your template now looks like this:

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

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:   

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

And, here’s your cart:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mko9o0vlsm5tfhnngnav.gif) 

## 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](https://github.com/contentful-developer-relations/contentful-vue-tutorial).

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](https://www.contentfulcommunity.com/) to seek help from fellow developers.

To expand on everything you’ve learned in this tutorial, [start building with Contentful](https://www.contentful.com/sign-up/) for free today — no credit card required.