DEV Community

Joel Varty
Joel Varty

Posted on

How to use Nuxt.js with a Headless CMS

Nuxt.js is a framework build on top of Vue.js that allows for Static Site Generation, Single Page Application and Server-Side-Rendering. What's amazing is that you can run the same code in all these different ways, and if we hook it up to a CMS, we can power all the content from there (including the Page Routing - cause I believe that's important).

DISCLAIMER: This tutorial uses Agility CMS, which I love, but I also work for this company. If that bothers you, please understand that you can apply this tutorial to many other Headless CMS vendors.

If you are just looking at using Vue.js for your app and don't need all the things that Nuxt has to offer, take a look at my previous post that shows you have to do that.

Dynamic Page Routing

We are going to be tell Nuxt how to render dynamic page paths right from our CMS. We'll be able to navigate these routes both statically and in SPA mode.

Most Headless CMS systems don't allow for Page Routing to be defined in the CMS itself, but Agility CMS has the ability to generate a Sitemap JSON in a Flat or Nested format, which allows you to render Pages that have Modules inside them in "Zones". See the Agility docs for more information on that.

alt text

As a developer, allowing your content editors to create pages in the sitemap wherever they want may sound scary. In reality though, this makes your life MUCH easier. Agility lets your editors add pages to the tree in the Content Manager, and it also lets you define Dynamic Pages, which are rendered from a list, such as blog posts or articles. This example will handle both.

Get the Code

The code I'm going to be walking you through is available in GitHub. Clone this repo to get started: https://github.com/joelvarty/agility-nuxt-app

The content you'll see when run the app is from an Agility CMS instance I created - at the bottom of this post I'll show you how to create your own free instance and hook up your API Keys to this code.

Run it Locally

As I said above, Nuxt is great because you can run it in "Universal" mode with Server-Side Rendering or in "SPA" mode with client rendering only.

I've created a couple different scripts for running this site in either mode.

# run the site in SPA mode with client rendering only
> npm run spa

# run the site in Universal mode with server rendering
> npm run dev
Enter fullscreen mode Exit fullscreen mode

Alt Text

When you run the site, you'll notice that I've included Vuetify in my Nuxt install. This adds some overhead, but looks pretty. If you click the hamburger menu in the top left, you'll see a navigation menu fly out that maps to the top level pages in the CMS.

Alt Text

If you click on the "Posts" page, you'll see a list of items in a list. Click on one, and you'll be taken to the "details" for that post. It's super simple, but it shows the potential.

Alt Text

Static Site Generation

Nuxt gives use a "generate" command that will troll through all our routes and generate static html files for all of them. I'll show you how we can take advantage of that below.

Under the Hood

Let's dig into what's actually happening here, so you can see how we're leverage the CMS API to build this site.

Configuration

Agility CMS requires us to input our GUID and API Keys, as well as a couple other values. I've put these in a file called agility.config.js

I've also specified the reference names of shared content lists that we'll need to grab the content from to render the site statically.

agility.config.js

export default {
    guid: '---', //Set your guid here
    fetchAPIKey: '---', // Set your fetch apikey here
    previewAPIKey: '---', // set your preview apikey
    languageCode: 'en-us',
    channelName: 'website',
    isPreview: false,
    caching: {
        maxAge: 0
    },
    sharedContent: ["posts"]
}
Enter fullscreen mode Exit fullscreen mode

Dynamic Routing

All the routes for Nuxt are normally stored in the pages folder, however we can also generate dynamic routes from our API using the extendRoutes method. Here we have catch-all to send all routes to a page component called AgilityPage.vue.

You also see that we've registered a middleware component that does most of the API access to the CMS. This is setup in the next.config.js

router: {
    middleware: 'agility-middleware',
    extendRoutes(routes, resolve) {
        routes.push({
            name: 'custom',
            path: '*',
            component: resolve(__dirname, 'agility/AgilityPage.vue')
        })
    }
},
Enter fullscreen mode Exit fullscreen mode

Static Page Routes

In addition to our dynamic routing, we can also generate dynamic routes from our API using the generate property our next.config.js

generate: {
    fallback: true,
    routes: function () {

        //generate all the routes from the sitemap API
        const agilityClient = new AgilityClient();
        const api = agilityClient.client;

        return api.getSitemapFlat({
            channelName: agilityClient.config.channelName,
            languageCode: agilityClient.config.languageCode
        }).then((sitemapFlat) => {

            return Object.keys(sitemapFlat).map((path, index) => {
                const retPath = index == 0 ? "/" : path;
                return {
                    route: retPath,
                    payload: sitemapFlat
                }
            })
        })
    }
}
Enter fullscreen mode Exit fullscreen mode

Working with Agility Page Templates and Modules

Agility CMS uses Page Templates to define how to render pages. We can use Components to render these, as well as the Modules that can be added to pages by Agility's content editors.

I've placed these in the components/templates and components/modules folder, and we register these in a file called agility.components.js.

You can see that I've got 4 modules and 1 page template registered below.

// Our Agility Modules 
import RichTextArea from './components/modules/RichTextArea'
import PostsListing from './components/modules/PostListing'
import PostDetails from './components/modules/PostDetails'
import Jumbotron from './components/modules/Jumbotron'

// Our Agility PageTemplates 
import OneColumnTemplate from './components/templates/OneColumnTemplate'

export default {
    moduleComponents: {
        RichTextArea,
        PostsListing,
        Jumbotron,
        PostDetails
    },
    pageTemplateComponents: {
        OneColumnTemplate
    }
}
Enter fullscreen mode Exit fullscreen mode

Page Template Components

Page Templates are comprised of one or more ContentZone modules, which have a name that was defined in Agility, and we can pass through the props that we get from the AgilityPage.vue component so that our module can use it.

<template>
  <div class="one-column-template">
    <ContentZone
      name="MainContentZone"
      :page="page"
      :pageInSitemap="pageInSitemap"
      :dynamicPageItem="dynamicPageItem"
      :sharedContent="sharedContent"
      :sitemapFlat="sitemapFlat"
      :sitemapNested="sitemapNested"
    />
  </div>
</template>

<script>
import ContentZone from '../../agility/AgilityContentZone'
export default {
  props: {
    page: Object,
    pageInSitemap: Object,
    dynamicPageItem: Object,
    sharedContent: Object,
    sitemapFlat: Object,
    sitemapNested: Array
  },
  components: {
    ContentZone
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

Module Components

Modules are also defined by components, and they are really easy and fast to build, since all the strongly typed content data from Agility is passed to it as props.

Here's the ultra-simple JumboTron module:

<template>
  <section class="jumbotron">
    <h1>{{item.fields.title}}</h1>
    <h2>{{item.fields.subTitle}}</h2>
  </section>
</template>

<script>
export default {
  props: {
    contentID: Number,
    item: Object,
    page: Object,
    pageInSitemap: Object,
    dynamicPageItem: Object
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

Deployments

We actually want to deploy this site twice: we want to be able to use it in SPA mode for Previewing content changes in the CMS before their are published live, and we want to have a Static mode deployment for production.

I've setup 2 different Netlify sites to handle these cases, and we can simply specify the Site ID in the deployment command to send the different output to each location. You may need to install the netlify cli first.

npm install netlify-cli -g
Enter fullscreen mode Exit fullscreen mode

SPA Mode for Preview

#BUILD SPA / PREVIEW SITE
> npm run build-spa

#DEPLOY SPA / PREVIEW SITE
> netlify deploy --dir=dist --prod --site=SITEID123XYZ
Enter fullscreen mode Exit fullscreen mode

See it running here: [https://nuxt-preview.netlify.com]

Static Production Mode

#GENERATE STATIC SITE
npm run generate

#DEPLOY PRODUCTION STATIC SITE
netlify deploy --dir=dist --open --prod --site=SITEID456ABC
Enter fullscreen mode Exit fullscreen mode

Check it out here: [https://nuxt-prod.netlify.com]

Do It Yourself

If you want to get started working with your own content in a CMS, I recommend the Free Tier of Agility CMS, which you signup for here.

Agility CMS Docs

Agility has a docs dedicated to getting you running with Content from its Content Fetch APIs. Check them out here.

Top comments (0)