Using Prismic with Nuxt.js

This article discusses how to use Prismic with Nuxt.js and how it's different than standard Vue.js. Migrating an old Prismic & Nuxt project? You can read how to do that here.

Why Nuxt.js?

Nuxt.js is a hugely popular solution within in the Vue.js community when it comes to creating server side rendered or statically deployed applications.

This community project is built on top of the Vue ecosystem and handles all aspects of a production-ready server side rendered app. Here's a link to learn more about Nuxt.js.

How to set up your Nuxt.js Project

With the help of James Pegg and the team at Nuxt.js we worked to create a plugin that makes creating Nuxt.js apps with Prismic a breeze.

Install dependencies

The first thing to do is install the @nuxtjs/prismic module.

Copy
npm i @nuxtjs/prismic

Make sure you're using at least Nuxt 2.11.0

Copy
npm upgrade nuxt

Configure your nuxt.config.js file

This file is where all the settings and configuration for the @nuxtjs/prismic module for your project will go.

Modules

Include Prismic in your modules like so:

Copy
{
  modules: [
    '@nuxtjs/prismic'
  ],
  prismic: {
    endpoint: "https://your-repo-name.cdn.prismic.io/api/v2",
    linkResolver: //path-to-html-serializer
    htmlSerializer: //path-to-html-serializer
  },
  generate: {
    fallback: "404.html"
  }
}

modules

This is where you add the plugin: @nuxtjs/prismic

prismic

This is where you add your settings in regards to Prismic

prismic.endpoint

This is where you define the link to your Prismic API to get all your content. You'll find your endpoint in your repo settings under the 'API & Security' tab.

prismic.linkResolver

Here you can either add directly or import your link resolver for your project. Learn more about the Link resolver.

prismic.htmlSerializer

Again here you can either add directly or import your custom HTML serializer. Learn more about the HTML serializer.

Generate

This small setting is for deploying sites statically on Netlify. This fallback will allow the 404 page to be loaded like a SPA so that previewing unpublished articles will function correctly.

Below is a full example of how this file should look.

Copy
export default {
  mode: 'universal',

  /*
  ** Headers of the page
  */
  head: {
    title: 'Prismic + Nuxt Blog example',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: 'Prismic + Nuxt Blog example' }
    ],
    link: [
      { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
      { rel: 'stylesheet', href: 'https://fonts.googleapis.com/css?family=Lato:300,400,700,900' }
    ]
  },

  /*
  ** Customize the progress-bar color
  */
  loading: { color: '#fff' },

  /*
  ** Global CSS
  */
  css: [
    '@/assets/css/resetr.css',
    '@/assets/css/common.css'
  ],

  /*
  ** Plugins to load before mounting the App
  */
  plugins: [
  ],

  /*
  ** Nuxt.js modules
  */
  modules: [
    // This is where you import the plugin
    '@nuxtjs/prismic'
  ],

  // This is where you configure your settings for the new plugin
  prismic: {
    endpoint: 'https://your-repo-name.cdn.prismic.io/api/v2',
    linkResolver: '@/plugins/link-resolver',
    htmlSerializer: '@/plugins/html-serializer',
  },

  /*
  ** Build configuration
  */
  build: {
    /*
    ** You can extend webpack config here
    */
    extend(config, ctx) {
      config.resolve.alias['vue'] = 'vue/dist/vue.common'
    }
  },

  // Netlify reads a 404.html, Nuxt will load as an SPA
  generate: {
    fallback: '404.html'
  }
}

Using the Html Serializer

One difference here between Nuxt and Vue is wrapping links from your rich text fields in the <nuxt-link> tag. Below is what a full HTML serializer for a Nuxt.js project should look like.

Copy
import linkResolver from "./link-resolver"
import prismicDOM from 'prismic-dom'

const Elements = prismicDOM.RichText.Elements

export default function (type, element, content, children) {
  // Generate links to Prismic Documents as <router-link> components
  // Present by default, it is recommended to keep this
  if (type === Elements.hyperlink) {
    let result = ''
    const url = prismicDOM.Link.url(element.data, linkResolver)

    if (element.data.link_type === 'Document') {
      result = `<nuxt-link to="${url}">${content}</nuxt-link>`
    } else {
      const target = element.data.target ? `target="'${element.data.target}'" rel="noopener"` : ''
      result = `<a href="${url}" ${target}>${content}</a>`
    }
    return result
  }

  // If the image is also a link to a Prismic Document, it will return a <router-link> component
  // Present by default, it is recommended to keep this
  if (type === Elements.image) {
    let result = `<img src="${element.url}" alt="${element.alt || ''}" copyright="${element.copyright || ''}">`

    if (element.linkTo) {
      const url = prismicDOM.Link.url(element.linkTo, linkResolver)

      if (element.linkTo.link_type === 'Document') {
        result = `<nuxt-link to="${url}">${result}</nuxt-link>`
      } else {
        const target = element.linkTo.target ? `target="${element.linkTo.target}" rel="noopener"` : ''
        result = `<a href="${url}" ${target}>${result}</a>`
      }
    }
    const wrapperClassList = [element.label || '', 'block-img']
    result = `<p class="${wrapperClassList.join(' ')}">${result}</p>`
    return result
  }

  // Return null to stick with the default behavior for everything else
  return null
}

Querying the API

When getting your content for your documents with Nuxt.js a good practice is to use the asyncData lifecycle function when requesting the data. When you do this you should pass the $prismic (this represents the @nuxtjs/prismic module) & error methods.

You can then create a try/catch block and use the $prismic method to query the API.

In the following example we use the getSingle helper function, as we would in standard Vue.js, to query a singleton type with the API ID of 'homepage'. Then we set the response as document and return the data to be used in the page template.

Copy
async asyncData({ $prismic, error }) {
  try{
    const document = (await $prismic.api.getSingle('homepage')).data
    return {
      document
    }
  } catch (e) {
    error({ statusCode: 404, message: 'Page not found' })
  }
},

Things to remember for Nuxt queries

When creating a query inside the asyncData method you cannot use the this keyword. Also when using Prismic Predicates to query your data you must use the all lowercase predicates.

So in the following examples:

Copy
this.$prismic.Predicates.at('document.type', 'post')

The query becomes:

Copy
$prismic.predicates.at("document.type", "post")

All other queries to the API remain the same. You can see these queries here.

Templating

Templating your data in Nuxt remains the same as vue.js except for one method where you deliver data from a rich text field as plain unformatted text.

In vue.js you can use richTextAsPlain to do this, but in Nuxt you will have to specify the method asText. You can see an example below which is templating a field with API ID title as plain unformatted text.

Copy
{{ $prismic.asText(title) }}

Previews

Thanks to the @nuxtjs/prismic module creating previews in Nuxt.js is really straightforward as everything is handled for you. All you have to do is set up your preview on the Prismic side.

Deploying your application

We recommend deploying your Nuxt.js and Prismic application with Netlify, but there are lots of other options that the Nuxt website explains in detail.

A full working example project with Nuxt.js

Feel free to explore our fully working example website project using Nuxt.js and Prismic.

Nuxt.js & Prismic Example Website