Start learning today

Choose Your Plan

Build real life apps. Learn by creating.

Pay Monthly

12

Pay Yearly

10

An Introduction to Headless CMS with prismic.io

Have you ever wondered what it means to build a website with a headless content management system (CMS)? Or even what a headless CMS is? Let’s take a look! It’s also known as a “decoupled”, and is an API-based content management system. By making the content accessible from an API, it allows the developer to create their website using whichever technology they prefer and simply query the API to retrieve the content for the site.

In this article, I’m going to show you the process I use when building a simple website using a headless CMS. I’ve set up an example website built with Node.js using the Express web framework, the Pug templating system, and prismic.io headless CMS.

Check out the working version of the website to get a feel for what we’ll be making.

The website contains a homepage, a few content pages, and a navigation. For each of these types of content, I start by defining all the content fields for the custom content type, then I query the API for that type of content, and finally fill my website templates with the retrieved content.

Let’s jump in and take a look at how to put together each of these content types.

1) The Homepage

Create the Homepage custom content type

We start by determining all the content fields we need for the type. For our website’s homepage, we need to define the content for the banner (an image, text, and button) and the different text sections that make up the rest of the page.

As mentioned above, the headless CMS I am using for this demo is prismic.io. When using prismic.io, you build your custom type using a drag-and-drop builder that outputs JSON code. You can view the code I used to build my custom types in the project files for this website example in the ‘custom_types’ folder.

If you are following along and creating your own working example, you can simply copy the custom type JSON code from the homepage.json file. Then paste the code into the JSON editor to create an exact copy of my custom type in your own repository.

Once we define all the content fields we need we will have a custom type that you can then fill in with our homepage content.

Next we’ll see how to query the API to retrieve this newly created content.

Query the API and load the homepage content

When using a headless CMS, you separate your content managment from your front-end app. This means you can use whatever technology you prefer to build your website. Many headless CMS providers (including prismic.io) provide development kits for all the major technologies, which make it easy to query the API and parse the json results.

For this project, I chose to work with Node.js, a javascript server-side platform. I like how easy and simple it is to use. And it was easy to get up and running using prismic.io’s Node.js development kit. The following code examples are for this particular technology, but you could use this general workflow for any technology you like!

Let’s setup our Node.js app to query the prismic.io API. First, we can create an Express middleware function to connect to the API.

// Connects to the API
app.use((req, res, next) => {
  Prismic.api(PConfig.apiEndpoint,{accessToken: PConfig.accessToken, req: req})
    .then((api) => {
      req.prismic = {api: api}
      res.locals.ctx = {
      endpoint: PConfig.apiEndpoint,
      linkResolver: PConfig.linkResolver
    }
    next()
  });
})

Then we can setup our homepage route. Using the middleware function above we can query the API specifically for our homepage and render its template.

We will query the homepage content by using the getSingle method provided in the development kit. This will retrieve the one and only document of the single type "homepage".

// Route for the homepage
app.route('/').get(function(req, res){

  // Query the homepage
  req.prismic.api.getSingle("homepage").then(function(pageContent) {

    // Render the homepage
    res.render('homepage', { pageContent: pageContent });
  });
});

Load the content into the homepage template

When we query the API, we will retrieve a JSON object that contains all of our content for the homepage. To load the content in our template, we can use the helper functions provided in the development kit.

Below you can see a portion of the homepage template which includes methods like pageContent.getImage("homepage.bannerImage").url to insert content into the template.

extends ./layout.pug

block body

  include ./partials/header.pug

  //- Homepage Banner Section
  section.page-banner.homepage-banner(style='background-image: linear-gradient(rgba(37, 63, 72, 0.92), rgba(37, 63, 72, 0.92)), url(' + pageContent.getImage("homepage.bannerImage").url + ')')
    div.banner-content.container
      p.banner-text !{pageContent.getText("homepage.bannerText")}
      if ( pageContent.getLink("homepage.buttonLink") && pageContent.getText("homepage.buttonText") )
        a.button(href=pageContent.getLink("homepage.buttonLink").url(ctx.linkResolver)) !{pageContent.getText("homepage.buttonText")}

  //- If there are any slices
  if pageContent.getSliceZone('homepage.body') !== null

    //- Display the slices    
    for slice in pageContent.getSliceZone('homepage.body').slices
      case slice.sliceType

        //- Text section
        when 'textSection'
          include ./partials/text-section.pug

        //- Quote section
        when 'quote'
          include ./partials/quote.pug

        //- Full width image section
        when 'fullWidthImage'
          include ./partials/full-width-image.pug

And that all we need to do to display our homepage using a headless CMS! Next up we’ll set up our other content pages.

2) Other pages

Create the Page custom content type

The design of the other pages is slightly different from the homepage, so we will make a custom content type specifically for it.

First we will define a unique identifier (UID) that we will use to query each page specifically. We will also need to setup the content fields for the banner (an image and text) as well as the rest of the sections of the page. For example we will need to define a text content field for the quote section.

As mentioned before, you can view the specifics of the custom type in the source code. Simply copy and paste the code for the Page custom type into the JSON editor to create your own copy of the Page custom type. Once properly set up, you should fill in a few pages with content so we have something to query.

Query the api and load the pages

Querying the pages will be a little bit different than the homepage. We will create a route for the pages and query them by their UIDs using the helper function getByUID. Here is what this might look like using NodeJS:

// Route for pages
app.route('/page/:uid').get(function(req, res) {

  // Define the UID from the url
  var uid = req.params.uid;

  // Query the page by its uid
  req.prismic.api.getByUID("page", uid).then(function(pageContent) {

    // Render the page
    res.render('page', { pageContent: pageContent });
  });
});

Load the content into the page template

Then it’s a simple matter of loading the page content into the page template.

extends ./layout.pug

block body

  include ./partials/header.pug

  //- Page Banner Section
  - var bannerImage = pageContent.getImage("page.bannerImage")
  - var bannerImageUrl = bannerImage ? bannerImage.url : ""
  section.page-banner(style='background-image: linear-gradient(rgba(37, 63, 72, 0.92), rgba(37, 63, 72, 0.92)), url(' + bannerImageUrl + ')')
    div.banner-content.container
      p.banner-text !{pageContent.getText("page.bannerText")}

  //- If there are any slices
  if pageContent.getSliceZone('page.body') !== null

    //- Display the slices
    for slice in pageContent.getSliceZone('page.body').slices
      case slice.sliceType

        //- Text section
        when 'textSection'
          include ./partials/text-section.pug

        //- Quote section
        when 'quote'
          include ./partials/quote.pug

        //- Full width image section
        when 'fullWidthImage'
          include ./partials/full-width-image.pug

3) Navigation

Create the Navigation custom content type

Finally, we need to set up our navigation custom type. It will include a title to identify it, as well as a group of page links (each with a link and the text to display in the navigation bar).

Query the api to load the navigation on all pages

The navigation content is displayed on all the pages, so we can create another Express middleware function to load it on all routes and save it to the results object which we can access from the templates.

// Query the site navigation with every route
app.route('*').get((req, res, next) => {
  req.prismic.api.getSingle("navigation").then(function(navContent){

    // Define the navigation content
    res.locals.navContent = navContent;
    next();
  })
});

Integrate the layout into the homepage and page templates

The very last step is to load the navigation content into both the page and homepage templates. We will do this by updating the header template and we will be done!

header.site-header
  div.logo
    a(href='/') Sitename.

  if navContent != null
    nav
      ul
        each link in navContent.getGroup('navigation.navLinks').toArray()
          li
            a(href=link.getLink("link").url(ctx.linkResolver))
              != link.getText("label")

And that’s it!

That’s what it takes to get started developing a website with a headless CMS. The level of control you have over how your website is put together and displayed makes this approach fast and fun.

If you want to explore this project further, you can quickly and easily get it set up on your computer and connected to prismic.io with the following commands on your terminal.

npm install -g prismic-cli
prismic theme https://github.com/levimykel/website-example-with-prismic

Then just add some content to your prismic.io repository and you can view the project. From your new project folder simply run the following commands.

npm install -g nodemon
nodemon

And just like that you have your very own working website with a headless CMS!