A couple of weeks ago, we open-sourced Markdoc, the authoring tool powering the Stripe docs. To accompany the launch, we published a blog post showing how to get started using Markdoc with Next.js, using the @markdoc/next.js plugin.
In this blog post, I’ll show you how to create your own plugin, using the one I wrote for Parcel, as an example, so that you can use Markdoc with it.
Before we get started, Parcel is a zero-configuration build tool that includes a development server, hot reloading, automatic production optimization and more, out of the box. However, it does not parse Markdown files by default so, to be able to use Markdoc with Parcel, you need a specific plugin. As Markdoc is an extension of Markdown, a standard Markdown plugin won’t be enough to parse files properly.
If you want to skip directly to the complete plugin, feel free to check out the repo.
Creating a plugin
Depending on the framework or tool you’d like to build a plugin for, they might handle their plugin system a little differently but the code contained in the plugin should be relatively similar. At its core, what needs to be done is call the Markdoc.parse()
and Markdoc.transform()
methods.
In Parcel, a plugin that transforms assets is called a Transformer.
Here’s what this Transformer looks like:
import { Transformer } from "@parcel/plugin";
import Markdoc from "@markdoc/markdoc";
export default new Transformer({
async transform({ asset }) {
const code = await asset.getCode();
const ast = Markdoc.parse(code);
const content = Markdoc.transform(ast);
asset.type = "js";
asset.setCode(`export default ${JSON.stringify(content)}`);
return [asset];
},
});
To start, you need to import Transformer
from Parcel, and Markdoc from @markdoc/markdoc.
Then, the way to access the content of files in a Parcel transformer is to call asset.getCode()
. From there, you have to call the .parse()
and .transform()
methods from Markdoc to handle the raw Markdown content.
Finally, you need to return the transformed content. Markdoc.transform()
returns a JSON object, so we need to specify that the type of the content is now JavaScript, by changing the .type
property to ‘js’. Then, you need to call the setCode
method to replace the content with the newly generated one and append it after export default
. This last part is specific to Parcel, however, calling parse
and transform
is required to create a plugin for any other tool.
Using a plugin
To use this plugin, you need to start by pasting the following lines in the .parcelrc
file in your application:
{
"extends": "@parcel/config-default",
"transformers": {
"*.md": [
"parcel-transformer-markdoc"
]
}
}
What this code does is extend Parcel’s default configuration to handle files ending in *.md
and applying the custom transformer to the content of these files.
Then, if you imagine a React app with a Markdown file called getting-started.md
, the code needed to display this content would look something like this:
import content from "./getting-started.md";
import Markdoc from "@markdoc/markdoc";
import React from "react";
export function App() {
const html = Markdoc.renderers.react(content, React);
return (
<main>
{html}
</main>
);
}
That’s it! Now, Markdown files will be parsed properly and you can use all the features Markdoc provides.
Other plugins
Other members of the developer community have also started making their own plugins. Feel free to check them out:
If you end up creating your own plugin, let us know!
📣 Follow @StripeDev and our team on Twitter
📺 Subscribe to our Youtube channel
💬 Join the official Discord server
📧 Sign up for the Dev Digest
About the author
Charlie Gerard is a Developer Advocate at Stripe, a creative technologist and Google Developer Expert. She loves researching and experimenting with technologies. When she’s not coding, she enjoys spending time outdoors, trying new beers and reading.
Top comments (3)
Marcdoc is really interesting
I checked Markdoc out. I have a question though.
Was it built to replace Markdown or to improve it?
Oh we're not trying to replace Markdown :) it's an extension, so we built it to be able to add more functionality like using variables and functions, custom components, doing syntax validation, etc.