NuxtContent v2 to v3 Migration

NuxtContent v3 Logo

When moving my blog files over to the new JAMStart repo and running the npm install it warned of errors in Nuxt Content 2. I upgraded to Nuxt Content 3 with npm audit fix --force and here are the issues I encountered and here is how I resolved them.

Migration and Installation

Nuxt Content v3 migration documentation can be found at:

  1. installation guide
  2. migration

Collections

The first concern was related to the breaking change introduced with Nuxt 3, i.e. collections.

WARN No content configuration found, falling back to default collection. In order to have full control over your collections, create the config file in project root.

Instead of defaulting to a single assumed collection of content stored in /content folder, you now define multiple collections and for each specify the root directory, files, and schemas in a new configuration file content.config.ts with the use of two new functions:

  1. defineContentConfig()
  2. defineCollection()

The basic structure of the content.config.ts looks like this.

import { defineContentConfig, defineCollection } from '@nuxt/content'

export default defineContentConfig({
  collections: {
    content: defineCollection({
      type: 'page',
      source: '**/*.md'
    })
  }
})

It took me a while to figure out is that the key name is the directory name for the collection. For example, in the basic configuration above, the key content said this collection root folder will be /content. If you want to put your content files for your collection in a different directory, for example in the /static directory, you would change the configuration like this.

export default defineContentConfig({
  collections: {
    static: defineCollection({
      type: 'page',
      source: '**/*.md'
    })
  }
})

Overlapping Collections

Note, as of v3.6.0 there is an issue with overlapping collection. It is noted in the documentation with

Currently, a document is designed to be present in only one collection at a time. If a file is referenced in multiple collections, live reload will not work correctly. To avoid this, it is recommended to use the exclude attribute to explicitly exclude a document from other collections using appropriate regex patterns.This topic is still under discussion in this issue: nuxt/content#2966.

Effectively for me this meant that I couldn't define a content collection for /content directory and blog collection mapped to /content/blog directory since the blog directory was within content directory. While there are ways to exclude and include using glob patters it was tricky.

Instead I achieved the same goal by specifying the /blog directory within the path variable of the content collection in the queryCollection.

const query = queryCollection('content')
    .where('path',  'LIKE', '/blog/%')
    .select('title', 'path', 'description', 'topic', 'dateCreated')

Collection Queries

queryCollection replaces queryContent()

queryCollection

FrontMatter Changes

I was using front matter variables to control how a page might behave. The variables manifest differently from NuxtContent v2 than in NuxtContent v3. Specifically, only the following front-matter variables are native by default:

  1. title
  2. description
  3. navigation

My other top level front-matter variables were not available at the top level (there were actually defined within the meta key, but these are consequently not easy to query against) You have to explicitly include them in the scheme of your collection definition. For example, to include a dateCreated date variable you have to add it to the schema key in the defineCollection in your content.config.ts file.

export default defineContentConfig({
  collections: {
    content: defineCollection({
      source: '**',
      type: 'page',
      // JAMStart custom Markdown Frontmatter Schema
      schema: z.object({
        dateCreated: z.date()
      })
    })
  },
})

Then you can easily query against dateCreated variable, and so forth.

Could not resolve import "#content/server"

Reviewing the NuxtContent documentation

$ Could not resolve import "#content/server" in C:\dev\JAMStart\server\routes\sitemap.xml.js using imports defined in C:\dev\JAMStart\package.json.

After investigation this appears to be an issue with the server/routes/sitemap.xml.js file which I had added to support the sitemap module. It's contents looked like this.

import { serverQueryContent } from '#content/server'
import { SitemapStream, streamToPromise } from 'sitemap'

export default defineEventHandler(async (event) => {
  // Fetch all documents
  const docs = await serverQueryContent(event).find()
  const sitemap = new SitemapStream({
    hostname: 'http://localhost:3001'
  })

  for (const doc of docs) {
    sitemap.write({
      url: doc._path,
      changefreq: 'monthly'
    })
  }
  sitemap.end()

  return streamToPromise(sitemap)
})

The module in package.json that needed was sitemap was v. 8.0.0 at NPM module site

{
  // ...
  "dependencies": {
    // ...
    "sitemap": "^8.0.0",
  }
}

NuxtContent recommends @nuxtjs/sitemap module which is a part of NuxtSEO

Steps to Resolve

  1. Uninstall "sitemap" - npm uninstall sitemap
  2. Remove the server/routes/sitemap.xml.js file
  3. Installed NuxtSEO "sitemap" - npm install @nuxtjs/sitemap
  4. Added the sitemap information to a new file called content.config.ts in the root.
import { defineContentConfig, defineCollection } from '@nuxt/content'
import { asSitemapCollection } from '@nuxtjs/sitemap/content'

export default defineContentConfig({
  collections: {
    content: defineCollection(
      asSitemapCollection({
        type: 'page',
        source: '**/*.md'
      }),
    ),
  },
})