Dynamic Routes on Nuxt.js

How to properly create dynamic routes using the generate command for building static websites on Nuxt.js.

Published on April 23, 2020

Wondering why Nuxt isn’t generating your blog posts after using the generate command? you must specify the routes yourself! But fear not, it’s really simple.

TL; DR

// nuxt.config.js

import fs from 'fs'
import * as path from 'path'

const getPaths = () =>
  fs
    .readdirSync(path.resolve(__dirname, 'posts'))
    .filter((filename) => path.extname(filename) === '.md')
    .map((filename) => `/blog/${path.parse(filename).name}`)

export default {
  generate: {
    routes: getPaths()
  }
}

Setup

First, make sure your folder structure looks like the code block below, it doesn’t have to be the same though. The _ prefix on a file name indicates a dynamic route.

pages/
--| index.vue
--| blog/
----| index.vue
----| _slug.vue

If everything checks out we are going to need a folder to host all of our markdown posts, so let’s create one at the root of our project. For the sake of this tutorial I’ll be using a generic name such as posts, more on that later.

mkdir -p posts

Within pages/blog edit the _slug.vue page. Make sure the asyncData function imports a post like the example below. The views (template section) can be anything you want.

<!-- pages/blog/_slug.vue -->

<template>
  <article>
    <header>
      <h1 v-text="title" />
      <p v-text="description" />
    </header>
  </article>
</template>

<script>
export default {
  async asyncData({ params }) {
    // import a post based on the url params
    // the `/blog/my-post` route will import `posts/my-post.md`
    const post = await import(`~/posts/${params.slug}.md`)

    return {
      // this will only work if you are extracting
      // front matter from the post using something
      // like `frontmatter-markdown-loader`
      // if that's not the case just return `post`
      // and render the whole thing
      title: post.attributes.title,
      description: post.attributes.description
    }
  }
}
</script>

Note: Parsing markdown front matter is not in the scope of this article but you read about it here.

Create a new post under our previously created posts folder.

# posts/my-post.md

---
title: My Title
description: My description.
---

...

If everything is setup, let’s get down to business.

The Solution

All we have to do is tell Nuxt to fetch our posts and map them to a url-like scheme so the generator knows what to render (and generate) alongside our static pages.

Add the following code to your Nuxt configuration file:

// nuxt.config.js

import fs from 'fs'
import * as path from 'path'

// our helper function
const getPaths = () =>
  fs
    // read all files in the `posts` folder
    .readdirSync(path.resolve(__dirname, 'posts'))
    // filter any `.md` file
    .filter((filename) => path.extname(filename) === '.md')
    // map to url, assuming your blog path is `/blog/`
    .map((filename) => `/blog/${path.parse(filename).name}`)

export default {
  // stripped
}

And finally, at the very bottom of the file, inside the export default bracket, tell the generate option to use our helper function.

// nuxt.config.js

export default {
  generate: {
    routes: getPaths()
  }  
}

You may now use the generate command safely and without any worries.

Fin

That’s it, thanks for reading.

how-todevnuxt

Marlos Pomin
Full Stack Developer & Retoucher based in Brazil, also a casual pentester.