Rendering markdown in Vue 3
Tags: vue, markdown
Published: 2022-09-15 09:51:16

## Introduction

Markdown can be incredibly useful for displaying and storing formatted text. For instance the famous readme.md files that can come with a repository.

The syntax is simple - yet effective. A rudimentary example for instance would be a table:

```markdown
| Syntax    | Description |
| --------- | ----------- |
| Header    | Title       |
| Paragraph | Text        |
```

Which results in the following:

| Syntax    | Description |
| --------- | ----------- |
| Header    | Title       |
| Paragraph | Text        |

Or perhaps, a list:

```markdown
- Bacon
- Ham
- Eggs
```

Touché:

- Bacon
- Ham
- Eggs

So, how does one turn the unformatted markdown text into something more suited for the viewing eyes?

Thankfully someone had already made a tool for this called [markdown-it](https://github.com/markdown-it/markdown-it), otherwise you'd have to make your own parser which would be a pain.

## Markdown-it

Markdown-it is a javascript markdown parser, exported as a function which produces pure HTML that someone can use however they like. Also it has a great array of plugins and features, as explained on the GitHub page.

Making a Vue component out of it is incredibly easy, the only requirement being that you install and import the `markdown-it` function and use the `render` method after setting a few parameters.

## Requirements and plugins

First things first - installing the dependencies.

The most important one being markdown-it itself:

`npm i markdown-it`

or

`yarn add markdown-it`

The other, more useful, plugins are recommended as some features are not supported by markdown-it by default:

- [markdown-it-abbr](https://github.com/markdown-it/markdown-it-abbr) - Abbreviatons
- [markdown-it-anchor](https://github.com/valeriangalliat/markdown-it-anchor) - Automatic IDs for anchors, useful for bookmarking certain parts of a page.
- [markdown-it-footnote](https://github.com/markdown-it/markdown-it-footnote)
- [markdown-it-highlightjs](https://github.com/valeriangalliat/markdown-it-highlightjs) - Incredibly useful for syntax highlighting
- [markdown-it-sub](https://github.com/markdown-it/markdown-it-sub) - A subscript
- [markdown-it-sup](https://github.com/markdown-it/markdown-it-sup) - A superscript
- [markdown-it-task-lists](https://github.com/revin/markdown-it-task-lists) - Formatted list of checkboxes

## Writing the component

After importing the `markdown-it` package and its plugins, one can generate an output using the render method:

```javascript
import MarkdownIt from "markdown-it";

const markdown = new MarkdownIt();

markdown.render("# Header");
```

Which should, as a result, produce the following:

```html
<h1>Header</h1>
```

You can also throw in the plugins we mentioned before:

```javascript
const markdown = new MarkdownIt()
  .use(MarkdownItAbbr)
  .use(MarkdownItAnchor)
  .use(MarkdownItFootnote)
  .use(MarkdownItHighlightjs)
  .use(MarkdownItSub)
  .use(MarkdownItSup)
  .use(MarkdownItTasklists);
```

The rest is just defining a string prop the component should use to display the desired text and using the regular Vue syntax:

```javascript
<template>
  <div v-html="markdown.render(source)" />
</template>

<script setup lang="ts">
import MarkdownIt from "markdown-it";
import MarkdownItAbbr from "markdown-it-abbr";
import MarkdownItAnchor from "markdown-it-anchor";
import MarkdownItFootnote from "markdown-it-footnote";
import MarkdownItHighlightjs from "markdown-it-highlightjs";
import MarkdownItSub from "markdown-it-sub";
import MarkdownItSup from "markdown-it-sup";
import MarkdownItTasklists from "markdown-it-task-lists";
import MarkdownItTOC from "markdown-it-toc-done-right";

const markdown = new MarkdownIt()
  .use(MarkdownItAbbr)
  .use(MarkdownItAnchor)
  .use(MarkdownItFootnote)
  .use(MarkdownItHighlightjs)
  .use(MarkdownItSub)
  .use(MarkdownItSup)
  .use(MarkdownItTasklists)
  .use(MarkdownItTOC);

defineProps({
  source: {
    type: String,
    default: ""
  }
});
</script>
```

And use as so:

```html
<MarkdownRenderer :source="'# Hi!!'" />
```