Including Markdown Content in a Vue or Nuxt SPA
We explore why markdown can be such a great choice for developers when they're creating a single page app with Vue.js or Nuxt.
Join the DZone community and get the full member experience.Join For Free
Developers love to show off a solution they've come up with to solve a tricky problem (heck, I'm doing it right now). For that reason, you'll probably create a developer blog at some point in your career to showcase your favorite hacks.
And, as a developer, you'll no doubt irrationally build your blog from scratch rather than use a pre-made solution, because that's just what we do!
Markdown is a really handy format for writing developer blog posts, as it makes it easy to include code blocks and other kinds of formatting without the verbosity of writing HTML.
If you are going to build a markdown-based developer blog, a Vue (or Nuxt) single-page app would be an excellent choice as we'll see in a moment.
Including Markdown Files
Including markdown files in a Vue SPA is actually a bit tricky. The biggest challenge is that each markdown file should be a "page" of your SPA. This means Vue Router needs to be aware of them, but since they're ever-changing content, you don't want to hardcode their URLs in the app code.
For the rest of the article, I'll be outlining an app architecture that deals with this.
Meta Info With Frontmatter
Often you'll want to include meta information about a post in the markdown file. For example, what is the banner image to be used, the meta description, the URL, the tags, etc.
I recommend using "frontmatter" for your markdown files, whereby the meta info as added as YAML data at the top of the file, like this:
We'll need frontmatter in this architecture to ensure that we can derive a URL from each new markdown file.
Serve Your Markdown Files
Make sure your markdown files are in a directory that is being statically served.
In a more sophisticated setup, we'd use Webpack to bundle the markdown, but I don't want to complete the key idea so we'll continue with this less efficient solution for now.
Generate a Manifest File
You should now generate a manifest file that contains each article's URL and path on the server.
Firstly, you'll need to decide on a set URL structure for each post e.g.
/:year/:month/:day/:title. Make sure this is derivable from the post by adding the required data to your frontmatter.
Now, create an executable script that will run during your build process. The script will iterate all your markdown files and generate a list of URLs and file paths in a JSON array which can then used by Vue Router.
Here's some pseudo code so you can see how it should work. Note that the frontmatter can be extracted using the front-matter NPM module.
You should end up with a JSON file like this:
Note that the generated manifest should also be statically served, as, in the next step, the SPA will grab it with AJAX and use it to dynamically add the routes.
Be sure to set up Vue Router to include a dynamic path that matches your article's URL structure. This route will load a page component that will, in the next step, display your markdown:
As it is, this dynamic path could match almost anything. How do we ensure that the provided URL actually matches an article? Let's grab the manifest, and before we attempt to load an article, ensure the URL provided is in the manifest.
In the created hook of your Vue instance, use AJAX and fetch this manifest file. The manifest data should be available to any component that needs it, so you can add it to your global bus or Vuex store if you're using one, or just tack it onto the Vue prototype:
Now, in your
Article component, when the dynamic route is entered, confirm if it's in the URLs provided in the manifest:
It'd be a good idea to fall back to a 404 page if
beforeRouteEnter returns false.
Loading the Markdown
Okay, so now the SPA is recognizing the correct URLs corresponding to your markdown content. Now's the time to get the actual page content loaded.
One easy way to do this is to use AJAX to load the content and parse it using a library like "markdown-it". The output will be HTML which can be appended to an element in your template using the
The big downside to this architecture is that the user has to wait for not one but two AJAX calls to resolve before viewing an article. Eww.
If you're going to use this approach, you really must use server-side rendering or prerendering.
The easiest way, in my opinion, is to use Nuxt. That's how I did it with this site.
Also, using Nuxt's
asyncData method makes it very easy to load in the manifest, and using the
verify method of each page component you can tell the app whether the article exists or not.
Plus, you can easily execute your generate manifest script as part of Nuxt's build process.
Bonus: Inserting Vue Components in the Content
A downside to using markdown for content is that you can't include dynamic content, i.e. there's nothing like "slots" in your markdown content.
There is a way to achieve, that, though!
Using the awesome frontmatter-markdown-loader, you can get Webpack to turn your markdown files into Vue render functions during the build process.
You can then load these render functions using AJAX instead of the markdown file:
This means you can include Vue components in your markdown and they will work! For example, on the Vue.js Developers blog, I insert an advertisement inside an article by adding a component like this:
Published at DZone with permission of Anthony Gore, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.