Building a Static Blog with Nuxt.js and Cockpit Headless CMS - Part 2: Dynamic Routes

In this post we'll be setting up our category page to display all posts that have a tag matching our category. We'll also be setting up our dynamic routes so that when we run "npm run generate" all the individual post and category routes will be correctly generated.

If you haven't read Part 1 of this guide you can find it here - Part 1: Setup

Generating our dynamic routes for individual blog posts

You might be wondering how can we generate static pages for each blog post when deploying or updating our site?

Nuxt comes with an easy solution for this, so open up your nuxt.config.js and add the following above the build: {...} property:

So what's going on here? Well first we make a call to our Cockpit backend to get our post entries. We then map this response into an object containing the actual route (we're using the title slug for this) and also a payload object.

The payload we set to the entire post entry. This will be passed to each generated blog post and we'll be able to access it and display the contents.

This makes generating our static site faster as we won't need to fetch each blog post individually from every blog post page we generate.

So now we've told our blog what routes it needs to have we need to create a page that will display the contents of individual blog posts.

The convention for dynamic pages in Nuxt is to name the page like so _title_slug.vue where title_slug is the unique route identifier in our case. Notice also we have prefixed title_slug with an underscore.

So create a new file called _title_slug.vue in the pages directory. If you want your links to be /blog/title_slug instead of just /title_slug then you need to create a blog directory in the pages directory then put _title_slug.vue in there. You can of course use /post/title_slug or whatever you like.

So as you can see we accept the payload as an argument in the asyncData method. We then check if we have the payload available (which is the post for that particular page in our case). If we do then we simply return it as post to the page data (you can check in Vue dev-tools).

If we don't have a payload i.e. when running our dev server then we simply send a post request to Cockpit. Notice the filter object in the request body that asks for the post with the same title_slug as the requested page. We can then check if this post exists in the response, if it does we return it and if not return the 404 error page.

Fire up the dev server again with npm run dev. You should have something that looks like this.

Now you may have noticed our markdown is not being parsed and it looks really messy. Don't worry we'll fix this soon!

Note: If you're looking for some markdown placeholder text you can use Lorum Markdown to generate some.

Parsing our Markdown

Let's sort out our markdown parsing and code higlighting.

npm install marked highlight.js --save-dev

We'll make a global filter that we can use to parse our Markdown so create a file called filters.js in the plugins directory and put this in it:

So here we use the same data returned from Cockpit as previously. Only this time we first collect the post entries into a const called collection.

For our tags we first map the collection into a new collection of just the post tags. Then we flatten this and call unique() on it to give us a collection of unique tags. (We would normally run flatMap() instead of calling map() and then flatten() however it wouldn't work as expected for me with collect.js)

With this unique collection of tags we map them into the route and payload properties like we did previously. For the tag payload we simply filter the original collection and return only post entries that have the specified tag.

For the posts we can simply map them directly into their route and payloads.

Finally we just call posts.concat(tags) to join the two together and return this.

So now we've got routes for our posts and a category page for each unique post tag!

Creating our Category page

Since we've set our category routes to be /category/tag-name we need to create a category directory inside the pages directory.

Inside the category directory create a new file called _tag.vue (following the same naming convention as before) and put the following inside: