Getting started with Markdoc in Next.js

Stripe is open-sourcing Markdoc, the Markdown-based authoring system that powers the Stripe documentation website. Whether or not it’s for a easy static website, authoring tooling, or a full-blown documentation web site, Markdoc is designed to develop with you, so you may create participating experiences regardless of how giant or complicated your documentation turns into. Let’s take a look at easy methods to get began.
The remainder of this tutorial is applied in Subsequent.js, utilizing create-next-app.



Set up

To start out utilizing Markdoc, you first want to put in it with:

npm set up @markdoc/markdoc --save
Enter fullscreen mode

Exit fullscreen mode

or

yarn add @markdoc/markdoc –-save
Enter fullscreen mode

Exit fullscreen mode

As this pattern app is utilizing Subsequent.js, you additionally want to put in the Subsequent.js plugin:

npm set up @markdoc/subsequent.js --save
Enter fullscreen mode

Exit fullscreen mode

Lastly, add the next traces to the following.config.js file:

const withMarkdoc = require("@markdoc/subsequent.js");
module.exports = withMarkdoc()({
 pageExtensions: ["js", "md", "mdoc"],
});
Enter fullscreen mode

Exit fullscreen mode

These traces assist you to use .js, .md and .mdoc recordsdata.

These additions to the config file allow you to jot down your docs in both JavaScript or Markdown file codecs. If you want to make use of Markdown recordsdata with one other framework than Subsequent.js, a plugin will probably be required.

Now that you just’re all arrange, let’s write some content material.



Utilizing Markdoc

To get began, find the pages folder that’s robotically generated when spinning up a Subsequent.js app with create-next-app, and create a brand new index.md file inside it.

Markdoc syntax is a superset of Markdown, particularly the CommonMark specification, so, on this file, you may write content material utilizing this syntax.

# Some title
## A subtitle
This can be a paragraph with a [link to your awesome website](https://your-awesome-website.com)
Enter fullscreen mode

Exit fullscreen mode

Markdoc is extensible so you can even use variables, capabilities, and create {custom} tags. For these, you must outline your content material in JavaScript and use Markdoc.remodel() and Markdoc.renderers.react() to render every little thing.
Let’s look into how to try this.



Variables

Variables are outlined in a config object and may then be utilized in your content material. Right here’s what a whole code pattern may appear to be earlier than we break it down and clarify the totally different items.

// config.js
export const config = {
 variables: {
   person: {
     title: "Justice Ketanji Brown Jackson",
   },
 },
};
Enter fullscreen mode

Exit fullscreen mode

// Web page.js
import React from "react";
import Markdoc from "@markdoc/markdoc";
import { config } from "./config.js";

const Web page = () => {
 const doc = `
 Whats up {% $person.title %}
 `;

 const content material= Markdoc.remodel(doc, config);

 return <part>{Markdoc.renderers.react(content material, React)}</part>;
};

export default Web page;
Enter fullscreen mode

Exit fullscreen mode

For instance, when you wished to show a person’s title, you’ll declare a config object like this:

 const config = {
   variables: {
     person: {
       title: 'Justice Ketanji Brown Jackson'
     }
   }
 };
Enter fullscreen mode

Exit fullscreen mode

In Markdoc, to reference the variables in your config, prepend the variable title with a $dollarSign. For instance, you’ll check with the person’s title like this:

const content material = `
Whats up {% $person.title %}
`;
Enter fullscreen mode

Exit fullscreen mode

Don’t overlook to prepend the variable with a $, in any other case it will likely be interpreted as a tag.

Then, it’s essential to move these two variables in Markdoc.remodel() and render your content material, utilizing Markdoc.renderers.react().

const content material= Markdoc.remodel(doc, config);

return <part>{Markdoc.renderers.react(content material, React)}</part>;
Enter fullscreen mode

Exit fullscreen mode

Utilizing variables is a strong characteristic, for example, if you wish to show dynamic information, corresponding to a person’s API key. An instance of such a characteristic could be discovered on the Stripe documentation website.



Customizing kinds

Markdoc introduces the idea of annotations to assist you to fashion totally different nodes, that are parts Markdoc inherits from Markdown.

For instance, you may add IDs and lessons with the next syntax:

// index.md
# My title {% #custom-id %}
# One other title {% .custom-class-name-here %}
Enter fullscreen mode

Exit fullscreen mode

You’ll be able to then refer to those in your CSS to use kinds.

// kinds.css
#custom-id {
 colour: purple;
}
.custom-class-name-here {
 colour: darkblue;
}
Enter fullscreen mode

Exit fullscreen mode

This might generate the next HTML:

<h1 id="custom-id">My title </h1>
<h1 class="custom-class-name-here">One other title </h1>
Enter fullscreen mode

Exit fullscreen mode

And render the content material proven beneath:

Some style-related attributes may also be utilized utilizing annotations:

Perform {% width="25%" %}
Instance  {% align="proper" %}
Enter fullscreen mode

Exit fullscreen mode



Tags

Markdoc comes with 4 built-in tags and likewise allows you to construct your personal. For instance, you may attempt the if tag that permits you to conditionally render content material.

const config = {
 variables: {
   tags: {
     featureFlagEnabled: true,
   },
 },
};

const doc = `
{% if $tags.featureFlagEnabled %}

Here is some secret content material

{% /if %}
`;
Enter fullscreen mode

Exit fullscreen mode

You’ll be able to construct your personal tags in three steps. Let’s undergo an instance with a {custom} banner element.
First, it’s essential to begin by creating the React element that you just wish to render. The code for a small banner element may appear to be this:

// Banner.js
const Banner = ({ sort, kids }) => {
 return (
   <part className={`banner ${sort}`}>
     {kids}
     <fashion jsx>{`
       .alert {
         border: 1px strong purple;
       }
     `}</fashion>
   </part>
 );
};

export default Banner;
Enter fullscreen mode

Exit fullscreen mode

This element will settle for a sort prop to point if the banner must be styled as an alert, data, warning banner, and many others. The kids prop represents the content material that will probably be handed inside that tag within the Markdown file in a while.

To have the ability to use this element as a tag in a Markdown file, first create a “markdoc” folder on the root of your Subsequent.js app, and a “tags.js” file inside it, that may include all of your {custom} tags.

This app’s structure would find yourself wanting like this:

parts/
|-- Banner.js
markdoc/
|-- tags.js
pages/
   index.md
Enter fullscreen mode

Exit fullscreen mode

Inside your {custom} tag file (right here tags.js), it’s essential to import your React element and export a variable containing the element you wish to show. You’d additionally embrace any attributes you wish to use. On this case, the kind of banner.

When declaring the attributes, it’s essential to specify their information sort.

// markdoc/tags.js
import Banner from "../parts/Banner";

export const banner = {
 Element: Banner,
 attributes: {
   sort: {
     sort: String,
   },
 },
};
Enter fullscreen mode

Exit fullscreen mode

The ultimate step is to make use of this practice tag in your Markdown content material, like this:

{% banner sort="alert" %}
That is an alert banner
{% /banner %}
Enter fullscreen mode

Exit fullscreen mode

Should you create a {custom} tag that doesn’t settle for any kids, you may write it as a self-closing tag:

{% banner/ %}
Enter fullscreen mode

Exit fullscreen mode



Syntax validation

Moreover, Markdoc allows you to validate the summary syntax tree (AST) generated earlier than rendering. Should you think about the Banner element written above, you should utilize it as a tag when writing your content material in JavaScript and test for any syntax error earlier than rendering.

For instance, if a banner tag is used with out a sort attribute that’s required, you may implement some error dealing with to keep away from rendering damaged content material.
This syntax validation could be applied with a single line, utilizing Markdoc.validate().

 const config = {
   tags: {
     banner,
   },
 };

 const content material = `
 {% banner %}
 Instance banner with a syntax error
 {% /banner %}
`;

 const ast = Markdoc.parse(content material);
 const errors = Markdoc.validate(ast, config);
 // Deal with errors
Enter fullscreen mode

Exit fullscreen mode

On this case, the error returned will appear to be this.

Screenshot of an error in the browser's dev tools with a message saying "Missing required attribute 'type'"



Capabilities

You’ll be able to prolong Markdoc with {custom} utilities by writing your personal capabilities. For instance, when you wished so as to add a technique to remodel your content material to uppercase, you’ll begin by making a file inside your markdoc folder, for instance capabilities.js. On this file, add the next helper operate:

// markdoc/capabilities.js

export const uppercase = {
 remodel(parameters) {
   const string = parameters[0];
   return typeof string === 'string' ? string.toUpperCase() : string;
 }
};
Enter fullscreen mode

Exit fullscreen mode

Then import this operate within the element that wants it, and move it in your config object:

import { uppercase } from "../markdoc/capabilities";

const config = {
 capabilities: {
   uppercase
 }
};
Enter fullscreen mode

Exit fullscreen mode

Name it in your content material inside {% %} tags:

const doc = `
 {% uppercase("Whats up, World") %}
`
Enter fullscreen mode

Exit fullscreen mode

And at last, name Markdoc.remodel() and use the React renderer to render every little thing.

const doc = Markdoc.remodel(doc, config);
Markdoc.renderers.react(doc, React);
Enter fullscreen mode

Exit fullscreen mode

So, the entire syntax for a small element would appear to be this:

// config.js
export const config = {
 capabilities: { uppercase },
};
Enter fullscreen mode

Exit fullscreen mode

// capabilities.js
export const uppercase = {
   remodel(parameters) {
     const string = parameters[0];
     return typeof string === "string" ? string.toUpperCase() : string;
   },
 };
Enter fullscreen mode

Exit fullscreen mode

// Web page.js
import Markdoc from "@markdoc/markdoc";
import { config } from "./config.js";
import { uppercase } from "./capabilities.js";

const Web page = () => {
 const doc = `
 {% uppercase("Whats up, World") %}
 `;

 const doc = Markdoc.remodel(doc, config);

 return <part>{Markdoc.renderers.react(doc, React)}</part>;
};

export default Web page;
Enter fullscreen mode

Exit fullscreen mode

Some built-in capabilities can be found corresponding to equals, to show some content material if two variables are equal, not/and/or to make use of in an if assertion to carry out boolean operations, default that returns the second parameter handed if the primary returns undefined, and debug that serializes the values as JSON that will help you whereas debugging.



Sources

Should you’re fascinated about studying extra, listed below are some helpful assets you may take a look at, and when you’re , we welcome your contributions to the repository! We will’t wait to see what you’re going to construct with Markdoc and don’t hesitate to tell us what you assume!

Official Markdoc documentation
Markdoc repository
Markdoc Next.js plugin repository
Markdoc playground
Next.js boilerplate demo



Keep related

As well as, you may keep updated with Stripe in just a few methods:
📣 Comply with us on Twitter
💬 Be a part of the official Discord server
📺 Subscribe to our Youtube channel
📧 Join the Dev Digest



Add a Comment

Your email address will not be published. Required fields are marked *