Originally published on www.contentful.com

  • tutorial
  • javascript
  • contentful
  • noframeworks

How to build a lightweight microblogging site with Contentful, vanilla HTML, CSS and JavaScript

I’m a big believer in the KISS principle in web development. KISS is an acronym for the phrase “keep it simple, stupid” — and while I don’t advocate for calling anyone “stupid” 🙈 — I definitely advocate for keeping things very simple when learning to work with new technology.

Before joining Contentful as a developer evangelist in January 2021, I purposefully avoided using Contentful in my front-end projects so that I could come to it with fresh eyes and explore it with purpose and meaning.

So, with this being my first-ever app built using Contentful (🎉), I wanted to make sure I focused my efforts on learning this single technology, rather than getting bogged down with other front-end frameworks and technologies such as Angular, React, Vue or Svelte (which are all great, by the way!).

What do you mean, no front-end frameworks?

thingoftheday is a simple static site built entirely with vanilla HTML, CSS and JavaScript; there are no packages to install, no build commands to run, and no framework patterns to follow.  

Sidenote: It was really fun to get back to basics using native JavaScript functionality such as document.querySelector and document.createElement in this app. As an added bonus, the single-page application is super fast and is deployed instantly (to Netlify) whenever the repository receives new changes.

Sounds great! How do I get started?

If you’re the type who likes to fork a repository and get stuck into the code straight away (I know I am!), I’ve included a quick-start guide on GitHub here where you can import the content type and example content defined in this tutorial using the Contentful CLI in a matter of minutes. You can also get started by watching this quick tutorial video.

Have fun!

Here’s what you’ll need to be able to build a lightweight microblogging site the old-school way with Contentful.

A Contentful account

You can sign up for a free Contentful account here.

Optional: Prepare your development environment

If you want to use es6 syntax in your vanilla JavaScript file, you’ll need to serve your module file to the browser over an http-server in your local development environment. You can do this with this nifty npm package, aptly titled “http-server.”

That’s everything you need!

Let’s get building!

Create your space in Contentful

Call it whatever you like. We’ll go with thingoftheday.

An animated GIF showing how to create a Contentful space

Create and define your content type

Before we write any code, we’re going to create our content type inside our space

What’s great about Contentful is that it encourages you to approach your applications with a data-first mindset. I have always advocated for building applications from the “middle out,”  which means defining your data structures and how they might be extended before beginning any development on a project. With this method, both backend and front-end development teams can get to work simultaneously. This is how agile teams work effectively, especially in large organizations. 

Your content is the most important building block of your applications. Without its data, your application doesn’t have a voice.

Wait, what’s a content type?

A content type is a collection of bits of data you’d like to group together. Think of it like an object in JavaScript. 

For example, you might have a content type of a cat. Your cat will have different fields that are used to describe it (or properties in JavaScript object land), which might be breednumberOfLegs or favoriteBrandOfCatFood. These fields will be of certain types (think text, date, media). If you’re familiar with strongly typed programming languages such as TypeScript, this might feel nice and familiar.

The purpose of thingoftheday is to capture a small image/snippet/tweet/link/thing each day that I’ve learned, or to record proud achievements that I wanted to remember forever that won’t get lost in a Twitter timeline.

The basic requirements for the microblog are to display the following per post:

  • Some text 

  • An image

  • An external link

  • Some text for the link 

  • A panther emote that represented my mood on the day

And this is what we’ll use to build our content type!

In case you’re wondering about the panther emote — or any of the panthers that you’ll see if you follow me online — let me explain. It started off as a bit of a tongue-in-cheek thing. My handle, @whitep4nth3r, is what I would call myself if I were a member of FSociety. (If you haven’t watched Mr. Robot, you need to.) The panthers just expanded from there. For example, if you watch my Twitch stream, you can express yourself with a plethora of panther emotes and drop different flavors of panther rain on me. I use these panthers to express my current mood at the time on my Twitch stream. The panthers have just become a “thing” — so they will be a “thing” for thingoftheday!

Here’s a quick video tutorial on how to set up the content type for our microblog.

We’ve now created our content type for our blog posts. 🎉

Here’s what the UI should contain when you click on the content model navigation tab.

A screenshot of a finished content model for thingoftheday

Now, for the content!

Let’s add our first microblog post!

Navigate to the content tab and click on add microblog.

Add some fun stuff, then click the big green publish button. 

An animated GIF showing the creation of a blog post

Navigate back to the content tab, and you’ll find your shiny new microblog post in the list. ✨

The text field has been used for the entry title in the name column as we specified earlier.

Image showing the published piece of content

Congrats! You’ve created your first post with your very own content type in Contentful. 😎

Next up, fetch your content

In order to fetch your content, you’ll need two pieces of data. Your space ID and an access token. You can read more about personal access tokens here.

Head on over to the settings tab, click general settings, and find your space ID at the top of the page.

Screenshot showing where to find the Contentful space ID

Click on the settings tab again, and click on API keys.

Click on the big blue add API key button, configure what you need and copy the content delivery API - access token.

A screenshot showing where to find the Contentful Delivery API access token

Click save. Now we’re ready to rock and roll!

The Content Delivery API

Contentful offers a few ways to fetch content, including the REST API and the GraphQL API. We’re going to use the GraphQL API for this application, because there’s no need to download any packages or SDKs, and, most importantly, it’s a fantastic way to request only the data you need for the front end. No need to deal with huge complex payloads with lots of irrelevant system information!

Remember — keep it simple, stupid! 🙈

Sidenote: If JavaScript isn’t your thing, check out how you can also use the GraphQL API with cURL, Python, Ruby and PHP here. Thanks Shy!

Let’s head over to Contentful’s GraphiQL interface using the following URL configured with your space ID and access token:

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

Here’s where we’ll construct our GraphQL query to use in our application code.

You might need to play around a bit with the format of your query depending on your content type fields (and whether or not you chose to include panthers!), but here’s some example code to get you started.

{
  microblogCollection {
    items {
      sys {
        firstPublishedAt
      }
      text
      image {
        url
        title
        width
        height
        description
      }
      panther
      link
      linkText
    }
  }
}

A key thing to note is that the name of your content type (i.e. microblog) will need to be queried as a collection, e.g. microblogCollection, with a property of items.

Notice that the query is constructed from the automatically generated field names we defined when we set up our content type. Give it a go!

You’ll also notice that I’m requesting some system information in this query (sys.firstPublishedAt). You’re free to add a separate date field to your microblog content type if you wish, instead of using the system information. Moreover, if you’re using the Contentful REST API, sys.created and sys.updated will be available for you to consume.

Finally, let’s write some vanilla front-end code!

Let’s set up a basic HTML boilerplate file and add links to our CSS file and JS file.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>thingoftheday by @whitep4nth3r</title>
    <meta name="description" content="A wonderful description">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <script type="module" src="app.js"></script>
  </body>
</html>

Since we’re using vanilla JavaScript, we’re going to use the native fetch api in our code.

The aim of this next part is to fetch our content from Contentful in our app.js file, and log it to the browser console.

Use the query you constructed in the GraphiQL interface, construct your fetch options and fetch that data!

//Here's our query we constructed in the GraphiQL interface
const query = `{
  microblogCollection {
    items {
      sys {
        firstPublishedAt
      }
      text
      image {
        url
        title
        width
        height
        description
      }
      panther
      link
      linkText
    }
  }
}`

// Here are our options to use with fetch
const fetchOptions = {
  spaceID: "yourSpaceId",
  accessToken: "yourAccessToken",
  endpoint: "https://graphql.contentful.com/content/v1/spaces/yourSpaceID",
  method: "POST",
  headers: {
    Authorization: "Bearer yourAccessToken",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ query })
}

// Let's fetch the data - check out the browser console!
fetch(endpoint, fetchOptions)
  .then(response => response.json())
  .then(data => console.log(data));

You might be worried that you’re using sensitive data (space ID and access token) in your code directly rather than using environment variables (as you would usually do in a framework like React), but don’t fret!

Contentful’s Content Delivery API (or CDA) is a read-only API. If you’re using Contentful to fetch public data, there’s nothing to worry about. If you would prefer to keep your Contentful credentials private, I would recommend proxying the data fetched from Contentful to your client via your own API server application.

Start your http-server and check out the console in your browser. 

We have content! 🎉

Checking the console in the browser for the fetched content

Let’s add our content to the DOM!

For each bit of data of each item in the returned array, we’ll want to create an HTML element and append it to the DOM.

Here’s a example function to get you started:

const addContentToDom = (items) => {
  items.forEach(item => {
    // Create the article element to hold all post data elements
    const newItemEl = document.createElement("article");

    // The panther field is required in our content type
    // We'll always have a panther!
    const newPantherEl = document.createElement("img");
    newPantherEl.src = `./panthers/${item.panther}.svg`;
    newPantherEl.alt = `${item.panther} panther emote`;

    // Append the panther element to the article element
    newItemEl.appendChild(newPantherEl);

    // Let's check if we have an image
    if (item.image) {
      // Create an image element
      const newImgEl = document.createElement("img");
      // Populate with data
      newImgEl.src = `${item.image.url}?w=500`;
      newImgEl.alt = item.image.description;

      // Add the image element to the article element
      newItemEl.appendChild(newImgEl);
    }

    // Let's check if we have some text
    if (item.text) {
      // Create an h2 element
      const newTextEl = document.createElement("h2");
      // Populate with data
      newTextEl.innerText = item.text;

      // Add the text element to the article element
      newItemEl.appendChild(newTextEl);
    }

    // Let's check if we have a link
    if (item.link) {
      // Create an anchor element
      const newLinkEl = document.createElement("a");
      // Populate with data      
      newLinkEl.href = item.link;
      newLinkEl.innerText = item.linkText || "View more";

      // Add the link element to the article element
      newItemEl.appendChild(newLinkEl);
    }

    // Let's append the new article element to the DOM!
    document.body.appendChild(newItemEl);
  })
}

// Don’t forget to pass the data from the fetch response correctly
// to your function!
fetch(endpoint, fetchOptions)
  .then(response => response.json())
  .then(data => addContentToDom(data.data.microblogCollection.items));

 You’ll now find your posts as unstyled HTML in the browser!

A screenshot of the HTML written to the DOM in the browser source code

With a little bit of date formatting, adding some classes to the HTML and some vanilla CSS magic, you’ll end up with something like this in no time. Now all you need to do is remember to add a daily post!

A screenshot of the styled microblog page

You can view the code for the application on GitHub here, and you can view my new microblogging site at thingoftheday.xyz. Feel free to fork it, play around with it and make it your own with your own content. I’ve also included a quick setup guide in the README file, where you can import the content type and example content defined in this tutorial using the Contentful CLI in a matter of minutes. 

If you’ve used this tutorial to help you get started with a lightweight microblogging site using Contentful, I’d love to check it out! Come and say hi on Twitch!

And remember: Build stuff, learn things, love what you do.