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

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.