How to build a project using Vue.js and Contentful

Illustration depicting the Vue.JS logo and a Contentful app, showing how to use Contentful and Vue together
Published
August 26, 2021
Category

Developers

Learn how to use Contentful to store and manage data for a Vue.js project. We’ll set up data in Contentful, use GraphQL to retrieve the data and Vue.js, one of the most popular front-end frameworks, to render an application. You can find the final code for this project on GitHub.

Project Overview

As someone who has seen dozens of musicals and plays over the years, I needed a way to keep track of them. I created TheaterLog — my first Vue project — to do just that. In this article, I add Contentful to an existing Vue project called TheaterLog.

Theater log screenshot

Before we get started, let’s talk a little about Vue. Vue.js is an open-source, progressive front-end JavaScript framework commonly used to build single-page applications and user interfaces for the web. It’s known to have a shallow learning curve with a welcoming and supportive community. You can get up and running with Vue.js even more quickly if you understand HTML, CSS and JavaScript.

Benefits of using Contentful

TheaterLog has helped me remember all of the shows I’ve seen but one aspect of the project could really use an improvement. Previously, I hardcoded all of the data about each show as an array of objects in a JavaScript file. While this worked initially, it became time-consuming to manage all of the information that I wanted to track. I’ve seen more than 90 shows, after all. It was time to move this data into Contentful. Let’s get started!

Set up a Vue project

I’m using TheaterLog as an example for this tutorial, but you are welcome to code along by creating a new Vue project or using an existing project.

Create a new Vue project

You can create a new Vue project using the Vue CLI. Vue CLI is a command-line utility that will build a scaffold of a Vue project and allow you to install and configure various tools such as TypeScript and CSS preprocessors.

Run the following commands in a terminal window.

To install Vue CLI: 

npm install -g @vue/cli

Now you can use the Vue command to create a new project. 

To create a new project:

vue create log-project

The last parameter of the command is the name of the project — in this example, we are using log-project. You can use a different project name if you prefer. 

A list with three options will be displayed. We will work with the default Vue 2 preset for this tutorial.

vue create log-project

Press enter to select the Vue 2 default. You will see a success message once the project has been successfully created. Follow the commands in the terminal to run the project. 

successful creation of log-project in vue

You can also use the Vue CLI to create a standard Vue 3 project, or you can manually configure all of the settings. Here is a list of what you can configure:

Selecting a preset in vue

Use an existing Vue project

If you already have an existing Vue project, all of the code in this tutorial can be added into your App.vue file or other component of your choice. For TheaterLog, I will add all of the Contentful code into my App.vue file so that I can share the data fetched from Contentful with other components. 

Now that we have a Vue project to work with, let’s head over to Contentful to set up our account and organize the project data.

How to set up Contentful

The first step will be to sign up for Contentful if you do not already have an account. Next, you want to create a space which is where all of your data will be housed within Contentful. Spaces are content repositories that store content types and entries

To create a space click on the Create a Space button within the Contentful dashboard. You can name this space whatever you like. I named my space TheaterLog.

Naming a space

How to create content types

Now it is time to create content types. A content type is a grouping of data. For example, in TheaterLog, my data consists of shows. Here is an example of what a JavaScript object for a show looks like:

{
   name: "Hamilton: An American Musical",
   link: "https://hamiltonmusical.com/new-york/",
   theater: "Richard Rodgers Theatre",
   date: "Oct 2016",
   location: "New York, NY",
   fav: true,
   upcoming: false,
   multi: 5,
   type: "musical",
   review: "love",
   price: 165
 }

The goal is to translate this JavaScript object into a content type within Contentful.

Click on the Content Model tab in Contentful. Then click on the Add content type button. Create a content type called Show. Now we can use existing data as a blueprint for how the content type in Contentful should be organized. 

GIF showing the creation of a content type in Contentful

How to manage content fields

A content type in Contentful comprises different fields to store data. Let’s add fields to the Show content type. Each show object includes key value pairs with data about a particular show such as the show Name, Date, and Theater. Create a field for each piece of data that needs to be included in your content type. Click on the Add field button to add your first field.

Add fields to a content type in contentful

Here you have the option to choose from a list of different field types that each come with specific built-in features. The date and time field, for example, has a date picker available. 

Contentful field types

Let’s create a few different types of fields together. 

First, we will create a field for the Name of the show. Click the Add Field button and select Text as the field type and then enter the field Name

Showing name settings in contentful

The Name field is the label for this field that will be shown in the Contentful UI. The Field ID, which is automatically generated from the Name value — though it can be changed at this point — will be how the field appears in the response from the API. 

Click on Create and configure to further customize the field. Under field options, select the “this field represents the Entry title” option. This will set the name field as the title of the entry, which is in our case, a show name. 

Entry title in Contentful

Click on the Validation tab. Here, you can specify what fields are required and set limitations on the values of each field. Let’s set this field to be required by selecting that option. This will stop entries from being saved if the name field has no value. 

Naming required fields in contentful

Click on the confirm button to save this field. 

Let’s create another field and explore more validation options in detail. 

Click on the Add Field button and select Text field. Name this field Rating. This field will store the rating for each show. In TheaterLog, emoji icons are used to signify the rating for a show. If I loved a show, I want to show the heart eyes emoji and if I thought a show was super sad, I want to show a crying emoji. Each emoji is attached to a specific icon, which is an image saved in an assets folder in the Vue project. 

Review images legend

In the code for TheaterLog, the emoji icons are displayed by their file names which match the rating name displayed below them. 

Let’s add this information to the field. Click on the Validation tab and click the “accept only specified values” checkbox. You can now add predefined values. Contentful will not allow an entry to be saved if the Rating field has any other value. Add a few rating values into the text box such as: funny, sad, and love.

Specified values rating in contentful

Click on the Appearance tab. This section will allow you to change how a field appears within the Contentful entry editor. Let’s change the appearance of this field to a dropdown.

Screenshot of rating dropwdown

Since we included predetermined fields, each of the values we entered in the Validation tab will now appear in this dropdown when editing or creating a new entry.

review dropdown menu

Let’s create one more field. Click on the add field button and select Boolean as a field type. This field will create a radio button where the options are yes or no (or true or false). Name this field Upcoming

This field will indicate if a show will be seen in the future, however the name Upcoming is pretty vague. Let’s add additional information about this field into the UI.

Click on the Appearance tab. In the Help text input, add a description of what data the field should contain. Add in “Is this a show that will be seen in the future?”

screenshot of upcoming help text

This text will appear in the entry editor and is a great way to give more context to each of your fields.

How to create reference fields

Most of the data for TheaterLog is specific to particular shows. However, there is some data that is shared between shows such as the theater. For example, I have seen nine shows at the John F. Kennedy Center for the Performing Arts in Washington, DC. 

With what we have learned so far, creating a text field to store the theater would seem like the most logical step. But what if we also want to keep track of the location of each theater as well?

This is where reference fields come in. Reference fields allow you to create links between content and most importantly, reference reusable content between entries. 

Let’s make a new content type for theaters to see it in action. Navigate to the Content Model tab and click the Add content type button. Create a new content type called Theater.

Screenshot of theater content type

Let’s add two fields to this content type. Create a text field called Name for the theater name and create a text field called City for the location of the theater. 

theater fields content type

Now we need to link reference the Theater content type within the Show content type.

Click on the Content Model tab in the navigation and click on the Show content type. Add a new field and choose Reference as the field type. Name this field Theater

Under the Validation tab, make this a required field and check the accept only specified entry type checkbox and choose Theater. This means that when you add a value to this field, it must be a Theater content type. This is helpful later when fetching data from the API.

Theater specified entry type

Now that you know how to create fields and content types, it’s time to create the rest of the fields for this content type. Here is an example of the types of fields you can create:

show field in contentful

You’re probably realizing that manually adding all of the fields could take a while! Contentful provides CLI tools that allow you to import pre-made content types and entries on the command line. Fork the GitHub repository for this project and follow the instructions to import completed TheaterLog content types and sample entries into your Contentful space. 

Now that content types and fields have been set up, you can now add a show to your project! To add a show, navigate to the Content tab and click the Add Show button. This will open the entry editor that will list all of the fields we added in the content type.

Show entry editor in contentful

 Enter in the data for the fields and click on Publish. You have created your first entry!

Hamilton entry publsihed

We’ve set up our content types and added our first show entry. Now let’s look at how we can retrieve that data in our Vue application.

Retrieving your data

Contentful offers two APIs to fetch your content — the Content Delivery API and the GraphQL API. GraphQL is a query language that allows you to define what data you request from an API. If you would like to learn more about GraphQL, check out our Learn GraphQL video series.

Now that we have data in Contentful, we will use the GraphQL API to retrieve the data. GraphQL is a great choice due to its ease of use, ability to specify exactly what data we want to retrieve and because it is self-documenting.

Before we can retrieve data from Contentful, we need the access token and space ID from your Contentful space.

To find the space ID, go to Settings and navigate to General Settings. 

Screenshot of space id

For the access token, go to Settings and click on API keys. Click on the add API key button and copy the content delivery API token. Click save and let’s start with the code.

screenshot of access tokens in contentful

Write your first GraphQL query

Now we need to build a GraphQL query. We can use Contentful’s GraphiQL tool in a browser by going to this link: 

https://graphql.contentful.com/content/v1/spaces/{YOUR_SPACE_ID}/explore?access_token={YOUR_ACCESS_TOKEN}

You can also install the GraphQL playground app within your Contentful space to explore and create GraphQL queries from inside the Contentful web app. 

What your query will look like depends on what data you would like to retrieve. Here is an example of a query that I created to get a list of all of the shows and all of the associated data:

{
  showCollection {
    items {
      sys {
        id
      }
      name
      link
      date
      price
      favorite
      upcoming
      multi
      type
      rating
      theater {
        name
        city
      }
      song {
        name
        videoLink
      }
    }
  }
}

Here is an example of what a query and result will look like in the GraphiQL tool:

graphqi query results screenshot

Using GraphQL in Vue

Now we can use our new GraphQL query within Vue to fetch our data. In TheaterLog, I have two components that are utilizing data about the shows — one displays the list of shows and the other calculates statistics. We can fetch the data in our App.vue file and pass the data down to child components using props.  

Within the App.vue file, all of the following code must go inside of the <script> tags. If you used Vue CLI to create your project, replace everything already within the <script> tag with the code below. We will fill in this template below:

// App.vue
<script>
export default {
  data() {
    // will store fetched show data here
  },
  created() {
   // will call method to fetch shows here
  },
  methods: {
    // will define function to fetch data here
  }
};
</script>

The first item we will add is a new function within methods called getShows

// App.vue
<script>
export default {
  data() {
    // ...
  },
  created() {
    // ...
  },
  methods: {
    getShows: async () => {
    // logic to fetch data from Contentful goes here
    }
  }
};
</script>

Within this method, we will create a new variable and set its value to be our GraphQL query that we built earlier. You may modify the query based on the fields you created in your Contentful space. For the query to return data, make sure you have created fields and published an entry in your Contentful space.

Let’s query for the show name, rating and theater for now.

// App.vue
<script>
  export default {
  data() {
    // ...
  },
  created() {
    // ...
  },
  methods: {
    getShows: async () => {
      const query = `{
       showCollection {
         items {
           sys {
             id
           }
           name
           rating
           theater {
             name
             city
           }
         }
       }
     }`;
    }
  }
};
</script>

Set up the code we need to fetch our data from Contentful. Here is where you will insert your space ID and access token that we saved at the beginning of this post. 

We will use environment variables to store the space ID and access token. Create a new .env file in the root of your project. Copy the following variables into the  .env file and replace the value with your space ID and access token.

// .env
VUE_APP_CONTENTFUL_SPACE_ID=SPACE_ID_HERE
VUE_APP_CONTENTFUL_ACCESS_TOKEN=ACCESS_TOKEN_HERE

We will create two new variables here. The fetchURL variable stores the API URL and the fetchOptions variable stores the request options. Note that we are calling the environment variables from the .env file within these variables. 

// App.vue
<script>
  export default {
    data() {
      // ...
    },
    created() {
      // ...
    },
    methods: {
      getShows: async () => {
        const query = `{
           showCollection {
             items {
               sys {
               id
             }
             name
             rating
             theater {
               name
               city
             }
           }
         }
       }`;
       const fetchUrl = `https://graphql.contentful.com/content/v1/spaces/${process.env.VUE_APP_CONTENTFUL_SPACE_ID}`;
       const fetchOptions = {
         method: "POST",
         headers: {
           Authorization: `Bearer ${process.env.VUE_APP_CONTENTFUL_ACCESS_TOKEN}`,
            "Content-Type": "application/json"
          },
           body: JSON.stringify({
            query
          })
        };
      }
    }
  };
</script>

Then we use the fetch API to make the request. 

// App.vue
<script>
export default {
  data() {
    // ...
  },
  created() {
    // ...
  },
  methods: {
    getShows: async () => {
      const query = `{
           showCollection {  
           // query goes here     
           }
         }`;

      const fetchUrl = `https://graphql.contentful.com/content/v1/spaces/${process.env.VUE_APP_CONTENTFUL_SPACE_ID}`;
      const fetchOptions = {
        method: "POST",
        headers: {
          Authorization: `Bearer ${process.env.VUE_APP_CONTENTFUL_ACCESS_TOKEN}`,
          "Content-Type": "application/json"
        },
        body: JSON.stringify({
          query
        })
      };

      try {
        const response = await fetch(fetchUrl, fetchOptions).then(response =>
          response.json()
        );
        return response.data.showCollection.items;
      } catch (error) {
        throw new Error("Could not receive the data from Contentful!");
      }
    }
  }
};
</script>

Now we need to add shows to data within the Vue component so that we can access this information in the template. 

// App.vue
<script>
export default {
  data() {
    return {
      shows: []
    }
  },
  created() {
    // ...
  },
  methods: {
    getShows: async () => {
      // ...
    }
  }
};
</script> 

We want to fetch the shows when the component instance is created, so we assign the value of shows in the created method on the component to the return value of the getShows method. 

// App.vue
<script>
export default {
data() {
   	  // ...
     },
 	async created() {
       this.shows = await this.getShows();
     },
 	methods: {
   	    getShows: async () => {
    // ...
    }
 	}
};
</script>

Now we have access to our data within our template! Here is the complete code:

// App.vue
<script>
export default {
 data() {
   return {
     shows: []
   };
 },
 async created() {
   this.shows = await this.getShows();
 },
 methods: {
   getShows: async () => {
     const query = `{
       showCollection {
         items {
           sys {
             id
           }
           name
		       rating
           theater {
             name
             city
           }
         }
       }
     }`;
     const fetchUrl = `https://graphql.contentful.com/content/v1/spaces/${process.env.VUE_APP_CONTENTFUL_SPACE_ID}`;
     const fetchOptions = {
       method: "POST",
       headers: {
         Authorization: `Bearer ${process.env.VUE_APP_CONTENTFUL_ACCESS_TOKEN}`,
         "Content-Type": "application/json"
       },
       body: JSON.stringify({ query })
     };

     try {
       const response = await fetch(fetchUrl, fetchOptions).then(response =>
         response.json()
       );
       return response.data.showCollection.items;
     } catch (error) {
       throw new Error("Could not receive the data from Contentful!");
     }
   }
 }
};
</script>

To test it out, you can console.log the response within the getShows method or you can view the data via the Vue developer tools

Now we need to loop through the list of items within our template. For each show, I am creating a list element that contains all of the information for that show. Each list item will be a card that looks like this:

theater log cards

The code for the template should go within <template> tags. If you created your project with the Vue CLI, replace everything existing in the template with the code below.

Here is the code for the wrapping list element. To loop through the items, we can use v-for and iterate through the shows array which we defined in our data earlier. Because it is a loop, you need to include a key. In the below example, we are using the entry ID from Contentful as the key.

// App.vue
<template>
  <ul class="shows">
    <li
      v-for="show in shows"
      v-bind:key="show.sys.id"
      class="show"
    > 
     ...additional info about the show here
    </li>
  </ul>
</template>

Within the list item, let’s add some information about the show:

// App.vue
<template>
   <ul class="shows">
     <li
       v-for="show in shows"
       v-bind:key="show.sys.id"
       class="show"
     >
       <div class="show-info">
         <p class="rating">Rating: {{ show.rating }}</p>
         <div class="show-name">
           <p>{{ show.name }}</p>
         </div>
         <div class="show-content">
           <p class="show-theater">{{ show.theater.name }}</p>
           <p class="show-location">{{ show.theater.city }}</p>
         </div>
       </div>
     </li>
   </ul>
</template>

In the browser you should now see an unstyled list with the type, name and theater of the shows that have been added to your Contentful space:

unstyled template

To complete the project, update the GraphQL query to fetch all of the fields that you require and render that data within the Vue template.

And that is it! TheaterLog is now fetching data from Contentful instead of pulling it in from a JavaScript file. Now it will be much easier to manage all the information I want to track for each show I have seen. 

The finished project

Gif of the final result of the theater log project

In this post, we successfully integrated Contentful into an existing Vue.js application. We learned how to set up Contentful by creating content types and fields. And to fetch our data, we utilized GraphQL. You can view the live version of the final project here. The code, including CSS styles, can also be found on GitHub with detailed setup instructions.This project most definitely can be remixed to track movies, concerts, TV shows or even books so have fun with it!

If you’ve followed this tutorial and have built something you’re proud of, let us know on Twitter using the hashtag #BuildWithContentful or post in the Contentful Community Slack!

About the author

Don't miss the latest

Get updates in your inbox
Discover new insights from the Contentful developer community each month.
add-circle arrow-right remove style-two-pin-marker subtract-circle