Was this page helpful?

Integrate Contentful with Nuxt.js

If you gave our new interactive getting started CLI 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:

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",

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: {
  // ...

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.

    <!-- render data of the person -->
    <h1>{{ person.fields.name }}</h1>
    <!-- render blog posts -->
      <li v-for="post in posts">{{ post.fields.title }}</li>

  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
          'sys.id': env.CTF_PERSON_ID,
        // fetch all blog posts sorted by creation date
          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,

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

This was a quick walkthrough what you can achieve in five minutes using our Interactive getting started CLI guide.

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.


Next steps

Not what you’re looking for? Try our FAQ.