Integrate Contentful with Nuxt.js
If you gave our starter templates guide a try you probably saw that the result is a universal JavaScript application that is capable of running Vue.js on the server side and in the browser. This means that you generate a whole set of static sites during build time and then if the user wants to navigate somewhere else the site will be partially rerendered using Vue.js. Pretty cool stuff!
This tutorial will show you how you can set up your own Nuxt.js project powered with Contentful.
Getting started with Nuxt.js
The recommended way to start with a Nuxt.js application is to use the recommended CI tool vue-cli.
$ npm install -g vue-cli
With the vue
command now being available you can initialize a new project with the nuxt/starter
template.
$ vue init nuxt/starter <project-name>
Now you only have to navigate into the newly created project, run npm install
and this is all it takes to make your universal JavaScript application ready for Contentful. If you're looking for more information check out the Nuxt.js getting started guide. The command npm run dev
will be available for development inside the project. When you run it you'll see the Nuxt.js example project. You can then tweak it to implement Contentful.
Setting up the content model using the Contentful CLI
Before you can start integrating Contentful you have to set up a space, create a content model and fill it with some data. If this is all new for you I recommend to read the intro to the Contentful data model and the the complete beginner's guide to creating content first.
While you can do all the data setup manually you can also use our CLI to create a new space and feed it with data. So let's do this instead.
You can install the CLI by one of the provided methods.
Now that you have the CLI available you have to authorize it via contentful login
. This command will open a new browser window in which you can create a new account or login to then retreive a new access token.
$ contentful login
Creating a new space is now only one CLI command away and you don't have to go to app.contentful.com to do it manually.
Note: Creation of a space may result in additional charges if the free spaces available in your plan are exhausted.
$ contentful space create --name 'Nuxt tutorial'
Great! A new space is now created and you're ready to seed it with content. The CLI tool will show you the space ID which you will need in a second.
To transfer content between spaces, use the contentful space export
and contentful space import
commands from the Contentful CLI.
The seed
command can be used with a defined --space-id
flag or but for convenience you can configure the CLI to use a specific space for the following commands, too.
$ contentful space use
? Please select a space:
...
❯ Nuxt tutorial (k8iqtj0xib4n)
...
It's time to import some data!
# after space selection using `contentful space use`
$ contentful space seed --template blog
# without space selection
$ contentful space seed --template blog --space-id <your-new-space-id>
Your new space has now a set up content model including a person and a blog post content types.
The last thing to do is to create a Content Delivery API access token. You can do this using the web app or also via the CLI.
$ contentful space accesstoken create --name nuxt-tutorial
✨ Successfully created access token nuxt (1234567890xxxxxxxxxxx)
This is enough to get started and integrate it into the Nuxt.js app.
Integrating Contentful into Nuxt.js
The Nuxt.js project at this point only has only the root index available which is defined in ./pages/index.vue
. To make it use Contentful we have to do two things:
- register the Contentful JavaScript CDA client library
- fetch data in the
index.vue
page component
Install the Contentful CDA JavaScript client library
The easiest way to use Contentful is by installing the JavaScript client library. You can do so by easily calling npm install contentful
. The JavaScript client library will be included in the browser JavaScript bundle so you should also save it as a production dependency using the npm --save
flag.
$ npm install --save contentful
Nuxt.js offers plugin functionality to make custom code available on the server (the static pre-rendering) and client side (the dynamic re-rendering). Fortunately the JavaScript client library is based on axios which makes it possible to use it in the Node.js and browser context.
To use it, create a new file in the plugins
directory called contentful.js
. The goal of this file is to create a client library client with pre-defined environment variables which we will set during the bootstrap process.
// ./plugins/contentful.js
const contentful = require('contentful');
// use default environment config for convenience
// these will be set via `env` property in nuxt.config.js
const config = {
space: process.env.CTF_SPACE_ID,
accessToken: process.env.CTF_CDA_ACCESS_TOKEN,
};
// export `createClient` to use it in page components
module.exports = {
createClient() {
return contentful.createClient(config);
},
};
Now you have to define the used environment variables. To avoid the need for setting all the environment variables in the CLI when running e.g. npm run dev
you can set up a new config file called .contentful.json
. This file includes the needed configuration:
- the entry ID of the person (the owner of the blog)
- the content type ID of blog posts to fetch the posts data
- your space ID
- the content delivery access token
You already used the space ID (CTF_SPACE_ID
) and the access token (CTF_CDA_ACCESS_TOKEN
). The content type ID for blog posts and the entry ID of the one person who is the author of the blog are already defined in the template data. The ID of the blog posts will be blogPost
and the ID of the perosn is 15jwOBqpxqSAOy2eOO4S0m
.
{
"CTF_PERSON_ID": "15jwOBqpxqSAOy2eOO4S0m",
"CTF_BLOG_POST_TYPE_ID": "blogPost",
"CTF_SPACE_ID": "YOUR_SPACE_ID",
"CTF_CDA_ACCESS_TOKEN": "YOUR_ACCESS_TOKEN"
}
In the nuxt.config.js
you can then require the config file and make it available in the plugins file via the env
property.
// ./nuxt.config.js
const config = require('./.contentful.json');
module.exports = {
// ...
env: {
CTF_SPACE_ID: config.CTF_SPACE_ID,
CTF_CDA_ACCESS_TOKEN: config.CTF_CDA_ACCESS_TOKEN,
CTF_PERSON_ID: config.CTF_PERSON_ID,
CTF_BLOG_POST_TYPE_ID: config.CTF_BLOG_POST_TYPE_ID,
},
// ...
};
The env
property is a way in Nuxt.js to define values that will be available via process.env
when run on in the Node.js context or the context object in the browser in Vue.js components. This becomes really handy and you will see why that is in a moment.
Fetch data and render every page
Nuxt.js defines conventions which defines what pages and routes should be available. The example template created already the file ./pages/index.vue
for you. This file will be the entry point of the site. It is also possible to define router with dynamic parameters. If you want to read more about that I recommend to read the Nuxt.js routing documentation or check the folder structure of our finished example.
In Nuxt.js you can define asynchronous data for every page component. This data will be fetched during build time and later before every page navigation. If you are not familiar with the Vue.js single file component approach make sure you understand the file structure first.
<template>
<div>
<!-- render data of the person -->
<h1>{{ person.fields.name }}</h1>
<!-- render blog posts -->
<ul>
<li v-for="post in posts">{{ post.fields.title }}</li>
</ul>
</div>
</template>
<script>
import { createClient } from '~/plugins/contentful.js';
const client = createClient();
export default {
// `env` is available in the context object
asyncData({ env }) {
return Promise.all([
// fetch the owner of the blog
client.getEntries({
'sys.id': env.CTF_PERSON_ID,
}),
// fetch all blog posts sorted by creation date
client.getEntries({
content_type: env.CTF_BLOG_POST_TYPE_ID,
order: '-sys.createdAt',
}),
])
.then(([entries, posts]) => {
// return data that should be available
// in the template
return {
person: entries.items[0],
posts: posts.items,
};
})
.catch(console.error);
},
};
</script>
These few lines will import the Contentful plugin you just wrote and create a new client library client. You see that Nuxt.js also provides shorthands for the plugins directory which makes it really easy to import our Contentful plugin (~/plugins/contentful.js
).
In the async
property of the exported object you can fetch the data you wish and return a promise that resolves with the data that should be available in the component. In this case you're fetching blog posts ordered by creation date and a single person. And you see that the configuration values for these calls are available via env
. You can then access the data in your template.
Side note: In theory you can save one API call as the person is linked in each blog post but for the sake of clarity we decided to make two calls here.
This is all it takes to have a first route pre-rendered by Nuxt.js. You can create more routes with more data coming from Contentful. This will lead then to several pre-rendered HTML pages and which then will be re-rendered automagically in browser during navigation. To make this production ready you can run npm run generate
to statically generate the pages. These pages will by default be written to dist
.
$ npm run generate
...
Asset Size Chunks Chunk Names
0.nuxt.bundle.19d1fc79d53508bafb3c.js 9.02 kB 0 [emitted] pages/index
1.nuxt.bundle.073636965192d97508b7.js 2.8 kB 1 [emitted] layouts/default
vendor.bundle.159c28fcc84fd619b9e6.js 213 kB 2 [emitted] vendor
nuxt.bundle.4a191451179ff5f8654a.js 24.5 kB 3 [emitted] app
manifest.2b2d08aa839a35d40759.js 1.51 kB 4 [emitted] manifest
index.html 132 bytes [emitted]
client-manifest.json 5.73 kB [emitted]
...
Sum up
Using Nuxt.js and Contentful you can create a universal JavaScript application rather quickly without much operationals overhead. You can simply build the stack and then push it to your CDN of choice and you're good to go.
If you want to see a final implementation check out our Blog in five minutes example. There you'll find a complete solution including several routes and advanced prerendering of routes.
Enjoy!
Next steps
Not what you’re looking for? Try our FAQ.