How to use Nuxt.js with a Headless CMS
Tags: cms, nuxt, vue, netlify
Published: 2019-08-28 16:41:29

[Nuxt.js](https://nuxtjs.org) is a framework build on top of [Vue.js](https://vuejs.org) 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](https://agilitycms.com/resources/guide/why-agility-includes-page-management-with-its-headless-apis)).

> 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](https://dev.to/joelvarty/using-agility-cms-with-vue-js-1aid) 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](https://help.agilitycms.com/hc/en-us/articles/360032554331-Page-Management-in-a-Headless-CMS) for more information on that.

![alt text](https://help.agilitycms.com/hc/article_attachments/360036409251/mceclip1.png "Agility CMS Page Routing")

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](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
```

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/kbzkhexsvpt8wek750it.png "Nuxt Agility App - Screen 1")

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](https://thepracticaldev.s3.amazonaws.com/i/nliax8zwlzaswt48741l.png "Nuxt Agility App - Screen 2")

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](https://thepracticaldev.s3.amazonaws.com/i/g4lxjtpn31w4vz39c1ts.png)

## 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
``` javascript
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"]
}
```

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

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

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

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

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

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

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

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

### 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:
``` html
<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>
```


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

#### SPA Mode for Preview
```
#BUILD SPA / PREVIEW SITE
> npm run build-spa

#DEPLOY SPA / PREVIEW SITE
> netlify deploy --dir=dist --prod --site=SITEID123XYZ
```

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

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](https://account.agilitycms.com/sign-up?product=agility-free).

## Agility CMS Docs
Agility has a docs dedicated to getting you running with Content from its Content Fetch APIs.  [Check them out here](https://help.agilitycms.com/hc/en-us/sections/360006070691-Content-Fetch-API).