FrontAid CMS - Agile Content Management with JSON & Git.
» FrontAid CMS / Blog
Using Nuxt Content with a JSON File
This post will show you how you can use Nuxt Content to read a JSON file with your content and use it to dynamically create pages based on it.
(We also have a similar post about Next.js.)
Let’s begin with a quick introduction in what tools and libraries we will be using for that. Nuxt.js is a framework for server-side rendered or statically generated applications. It combines the Node.js backend and frontend written in Vue.js to a single universal application. Nuxt Content is a Nuxt.js plugin that adds features to load arbitrary content files and use them with Nuxt.js. FrontAid CMS is a Content Management System (CMS) that is based on Git and generates a content file in the JSON format. While we will explain how Nuxt Content works with a JSON file that is managed with FrontAid CMS, this whole post is also applicable to other use cases. For example, you can use any other headless CMS or whatever tool you like to emit JSON. As long as you are dealing with Nuxt Content and JSON files, then this post is for you.
This is what we will be doing: We are going to load a JSON file to be used with Nuxt Content. Then we will use that file’s content to manually extend static pages and also to create pages dynamically. Thus, we will extend the router and create a dynamic navigation. The static page and the dynamic pages will be filled with their respective content. Note that you can find the complete example code on GitHub.
File Structure
The main files and directories that we will use are the following. They consist of components that are shared among different files, and the content directory where you place your content JSON file(s). Then there is a simple layout file and a static index page. We also added two configuration files, one for our own usage and one for the Nuxt.js configuration. And then there is also a package.json file that contains the dependencies and some scripts. Finally, there is a file called frontaid.model.json which includes the data model that is used with FrontAid CMS. This file is not needed if you don’t use FrontAid (yet).
- components/
- MetaPage.vue
- Navigation.vue
- content/
- frontaid.content.json
- layouts/
- default.vue
- pages/
- index.vue
- conf.js
- nuxt.config.js
- package.json
- frontaid.model.json
Setup
Now let’s start setting this all up. If you use FrontAid CMS, create a Git repository or use an existing one. Then read Setup & Git Integration to learn how to set it up properly. Make sure to place your JSON content file within the content directory so that Nuxt Content will be able to load it. If you don’t use FrontAid CMS, just place your JSON file in the same directory. The actual content of the JSON file will be covered in the next section. For now, just make sure the file exists.
We are using npm and thus create a package.json file with the content below.
That defines some scripts that are used with Nuxt.js, and also adds the necessary dependencies.
After you create that file, execute npm install
on the command line.
{
"name": "nuxt-content",
"scripts": {
"dev": "nuxt",
"build": "nuxt build",
"generate": "nuxt generate",
"start": "nuxt start"
},
"dependencies": {
"@nuxt/content": "1.7.0",
"nuxt": "2.14.3"
}
}
We are going to load a single JSON file in different places in our example project. To avoid repeating its name, we create a configuration file that we can reference instead. So create a file called conf.js and add the following code. The JSON file that we are using is called frontaid.content.json, but you can name it however you want. Just note that we omitted the file extension here.
export default {
CONTENT: 'frontaid.content',
};
Then you need to prepare the Nuxt.js configuration file called nuxt.config.js.
Add @nuxt/content
to the list of modules
and add an empty object literal to the router
property.
We will extend that in the navigation section.
export default {
modules: ['@nuxt/content'],
router: {},
};
And finally, add the file default.vue into the layouts directory.
This will be used as the layout for all the pages.
It is deliberately kept simple and only contains the navigation component that we will introduce later, and the default <Nuxt/>
element.
<template>
<div>
<Navigation/>
<Nuxt/>
</div>
</template>
<script>
import Navigation from '../components/Navigation';
export default {
components: {
Navigation,
},
};
</script>
Content Directory
Now it gets a little more interesting as we are finally adding actual content. If you are using FrontAid CMS, create a file called frontaid.model.json and add the following model content. You can even extend the model however you like. Then commit and push that file to your Git repository. FrontAid CMS will pick it up and present a user interface that allows you to fill in the content JSON file in the specified structure. Make sure to add some content and hit “save”. Then Git pull the updates onto your local project to continue.
If you don’t use FrontAid CMS (yet), you can fill the content file yourself. Just make sure to place it in the content directory and name it according to the conf.js file. We named it frontaid.content.json. If you are not creative, you can just add the following example content.
{
"title": "FrontAid CMS with Nuxt Content",
"index": {
"title": "Index Page",
"content": "<p>Some <strong>index</strong> content...</p>"
},
"pages": [
{
"title": "Foo Page",
"path": "/foo",
"content": "<p>Foo content...</p><h2>Heading</h2><p>Some other content...</p>"
},
{
"title": "Bar Page",
"path": "/bar",
"content": "<p><strong>Bar</strong> content...</p>"
},
{
"title": "Baz Page",
"path": "/baz",
"content": "<p><strong>Baz</strong> content...</p>"
}
]
}
Static Pages
Now let’s create our first page. Nuxt.js creates pages automatically from the files that are placed in the pages directory. And with Nuxt Content, you can extend those static pages with content from the content directory. In our example, we have the landing page defined as a static page. Its file name is index.vue and it contains the code below.
<template>
<main>
<h1 v-html="content.index.title"></h1>
<div v-html="content.index.content"></div>
</main>
</template>
<script>
import conf from '../conf';
export default {
head() {
return {title: this.content.title};
},
async asyncData({$content}) {
const content = await $content(conf.CONTENT).fetch();
return {content};
},
};
</script>
The script
part loads the content data (Nuxt Content calls it “data fetching") using asyncData
and adds the content
property to the Vue instance (i.e. to this
).
The head
function then picks it up and sets the document title (i.e. the <title>
tag) to whatever is defined in the global title
property of the content file.
The template
section also uses the content
property.
Specifically, it uses index.title
and index.content
to define its title and main content, respectively.
Both properties are using the v-html
attribute/directive so that HTML from the content file is not encoded but actually being used as HTML.
Dynamic Pages
While by now we have a static page that is enhanced with dynamic content from a JSON file, we also want to create whole pages dynamically.
To do just that, we first need to create routes dynamically which we can do by using the extendRoutes
function as can be seen below.
This has to be added to the router
property in nuxt.config.js.
The function will first load Nuxt Content, and then fetch our actual JSON content file.
Based on that, we can iterate through all the pages
that are defined in our content file.
For each one of them, we push an object to the routes
array so that Nuxt.js knows about them.
Each page adds its path
, defines a component
, and passes the route meta
field.
The meta
field is a generic way to pass arbitrary data to a component.
And the component itself is just a generic template that we will use for all the dynamic pages that we create.
async extendRoutes(routes, resolve) {
const {$content} = require('@nuxt/content');
const {pages} = await $content(conf.CONTENT).fetch();
pages.forEach(page => {
routes.push({
path: page.path,
component: resolve(__dirname, 'components/MetaPage.vue'),
meta: page,
});
});
}
The MetaPage.vue that is referenced in the extendRoutes
function above is responsible for using all the meta
information that we pass to it.
Make sure to create the file with the code below and store it into the components directory.
The component creates a computed property named page
that is just a shortcut to the meta
information that we passed earlier.
The template
section picks it up and shows the corresponding title
and content
properties.
And in the script
section you can also see that we again load the JSON content file.
We use the global website title from the content file to set the document title in the format “{Current Page Title} | {Generic Website Title}".
<template>
<main>
<h1 v-html="page.title"></h1>
<div v-html="page.content"></div>
</main>
</template>
<script>
import conf from '../conf';
export default {
head() {
return {title: `${this.page.title} | ${this.content.title}`};
},
computed: {
page() {
return this.$route.meta;
},
},
async asyncData({$content}) {
const content = await $content(conf.CONTENT).fetch();
return {content};
},
};
</script>
With both the dynamic routes and the static page set up, Nuxt.js already creates all the necessary files. But what is still missing is a navigation that lists all static and dynamic pages.
Dynamic Navigation
In this section we are going to add a global navigation to all the pages that we create. Think of it as a sitemap that lists and links to all the pages. Create the file Navigation.vue in the components directory and add the code below. This component is used in the default layout component to add the navigation to every single page.
<template>
<nav>
<ul>
<li>
<nuxt-link to="/">{{ content.index.title }}</nuxt-link>
</li>
<li v-for="page in content.pages">
<nuxt-link :to="page.path">{{ page.title }}</nuxt-link>
</li>
</ul>
</nav>
</template>
<script>
import conf from '../conf';
export default {
data() {
return {content: null};
},
async fetch() {
this.content = await this.$content(conf.CONTENT).fetch();
},
};
</script>
You can see that in the script
section we do not use asyncData
as we did before.
The reason for that is that the asyncData
handler is only available for page components.
The navigation component is a generic non-page component, though.
So we can’t use that handler but have to use fetch
instead.
And we use it to again load the JSON content file and store it into the reactive content
instance property.
Now we can use it in the template
section which is essentially a list of our pages.
It adds the static index page, and then also adds a list item for every single dynamic page we defined earlier.
And now that we have everything set up, you can update the JSON content file however you want. For example, you can update the project title or add new dynamic pages. And thanks to Nuxt Content and Nuxt.js, everything will be adapted accordingly. Nuxt Content is a great way to create dynamic applications based on JSON as a data source. We are essentially using a JSON file as a CMS. And if you are using FrontAid CMS, you or your copy texters can manage that file easily.