Joel Varty
Posted on August 28, 2019
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.
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
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.
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.
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"]
}
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')
})
}
},
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
}
})
})
}
}
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
}
}
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>
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>
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.
Agility CMS Docs
Agility has a docs dedicated to getting you running with Content from its Content Fetch APIs. Check them out here.
Posted on August 28, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.