Vue Components in Markdown With Vue Remark [Gridsome Guide]

November 13, 2020 · Updated February 17, 2021 · 3 min read

This is part eighteen of a series on building a file-based blog from scratch with Gridsome. Find the full series here.

Have you ever wondered how you can get dynamic content inside your blog posts?

Stuff like CodePens, image carousels, or quizzes?

That's totally possible in Gridsome with markdown posts.

vue-remark to the rescue!

At the end of this article, you will know how to add dynamic Vue components to your blog posts. We will build a spoiler section that you can show and hide by clicking a button.

Configure vue-remark for markdown components

First, install the package: yarn add @gridsome/vue-remark.

Then, we will replace the current @gridsome/source-filesystem with @gridsome/vue-remark in gridsome.config.js:

const tailwindcss = require('tailwindcss')
const autoprefixer = require('autoprefixer')

module.exports = {
  siteName: 'Awesome Blog',
  siteDescription: 'Blog about awesome lists, collections of resources around a specific technology.',
  siteUrl: process.env.DEPLOY_URL || 'https://blog.awesome',
  metadata: {
    author: 'Simon Mannes',
    twitter: {
      site: '@simon_mannes',
      creator: '@simon_mannes'
    }
  },
  plugins: [
    // add the following config for @gridsome/vue-remark
    {
      use: '@gridsome/vue-remark',
      options: {
        typeName: 'BlogPost',
        baseDir: './content/blog',
        route: '/:slug',
        template: './src/templates/BlogPost.vue',
        plugins: [
          ['gridsome-plugin-remark-shiki', { theme: 'nord', skipInline: true }]
        ],
        refs: {
          tags: {
            typeName: 'Tag',
            create: true
          }
        }
      }
    },
    {
      use: '@gridsome/plugin-sitemap',
      options: {
        exclude: ['/privacy', '/legal']
      }
    }
  ],
  templates: {
    Tag: '/tag/:id'
  },
  css: {
    loaderOptions: {
      postcss: {
        plugins: [
          tailwindcss,
          autoprefixer
        ]
      }
    }
  }
}

Then we need to edit the BlogPost.vue component:

<template>
  <Layout>
    <h1 class="mb-2 text-2xl font-semibold text-gray-900 dark:text-gray-100">
      {{ $page.post.title }}
    </h1>
    <p class="mb-4 font-light text-gray-700">
      {{ $page.post.date }}
    </p>
    <div class="flex flex-wrap mb-4 text-sm">
      <g-link
        v-for="tag in $page.post.tags"
        :key="tag.id"
        :to="tag.path"
        class="px-2 py-1 mb-4 mr-4 rounded-full bg-gray-300 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:text-gray-300 hover:bg-gray-700 dark:hover:text-gray-700 dark:hover:bg-gray-300"
      >
        {{ tag.title }}
      </g-link>
    </div>
    <!-- This is the figure element we added: -->
    <figure
      v-if="$page.post.image"
      class="flex flex-col"
    >
      <g-image
        :alt="$page.post.image.alt"
        :src="$page.post.image.path"
        class="mb-2"
      />
      <figcaption
        class="self-center mb-10 image-caption"
        v-html="$page.post.image.caption"
      />
    </figure>
    <!-- change the following div -->
    <div
      class="mb-16 markdown"
    >
      <VueRemarkContent />
    </div>
  </Layout>
</template>

<page-query>
query Post ($id: ID!) {
  post: blogPost(id: $id) {
    title
    date (format: "MMMM D, Y")
    summary
    path
    tags {
      id
      path
      title
    }
    image {
      alt
      path
      caption
    }
  }
}
</page-query>

When we now start the blog with yarn develop, our blog renders like before.

Creating a spoiler component

Let's create a Vue spoiler-component!

Luckily, Browsers support a native "spoiler component".

Here's the code for src/components/SpoilerSafe.vue:

<template>
  <details class="p-4">
    <summary>{{ summary }}</summary>
    <slot />
  </details>
</template>

<script>
export default {
  props: {
    summary: {
      type: String,
      required: true
    }
  }
}
</script>

Add component to a blog post

We can then use this component inside our blog posts like this:

---
slug: "fourth-post"
date: "2030-07-30"
title: "The Future of Design"
tags: ["future", "design"]
summary: "Synth pinterest bespoke, taiyaki williamsburg chambray cloud bread readymade."
---

import SpoilerSafe from '~/components/SpoilerSafe.vue'

...

<spoiler-safe summary="Reveal spoiler">
Banh mi authentic fashion axe affogato shoreditch umami bicycle rights keytar put a bird on it drinking vinegar pitchfork taxidermy. Synth pinterest bespoke, taiyaki williamsburg chambray cloud bread readymade.
</spoiler-safe>

...

Our spoiler component now looks like this:

spoiler component

That's It!

That’s it for this post on Markdown widgets and how we can use them in our Gridsome blog.

This is also the last planned part for my Gridsome series!

If you have more questions about Gridsome or have another idea for an article, shoot me an email at simon@mannes.tech.

Ok, I wrote another short post on how to include markdown files inside other markdown files with the Gridsome Vue Remark plugin.