Using Markdoc with Next.js
Using the @markdoc/next.js
package/plugin allows you to create custom .md
and .mdoc
pages in your Next.js apps, and automatically render them with Markdoc.
To get started right away, check out this starter repo. The quickest way to deploy your own version of the starter is by deploying it with Vercel or Netlify by clicking one of the buttons below.
Setup
This guide assumes that you already have Next.js installed. If you're starting from scratch, follow these steps for getting started with Next.js.
Follow these steps to get started with @markdoc/next.js
.
Install
@markdoc/next.js
and@markdoc/markdoc
:npm install @markdoc/next.js @markdoc/markdoc
Update your
next.config.js
const withMarkdoc = require('@markdoc/next.js'); module.exports = withMarkdoc(/* [options](#options) */)({ pageExtensions: ['md', 'mdoc', 'js', 'jsx', 'ts', 'tsx'] });
Create a new
.md
file in within/pages/
, such asgetting-started.md
pages ├── _app.js ├── docs │ └── getting-started.md └── index.md
Add some Markdoc to your file:
--- title: Get started with Markdoc description: How to get started with Markdoc --- # Get started with Markdoc
Or, clone this starter repo and follow the directions in the README.
Options
You can pass options to withMarkdoc
to adjust how the plugin behaves.
Option | Type | Description |
---|---|---|
schemaPath | string | Path to your Markdoc schema folder. See schema customization. |
mode | 'static' | 'server' | Determines whether the generated Markdoc pages use getStaticProps or getServerSideProps . |
For example, this is how you set the mode
to static
to pre-render the page at build time using the props returned by getStaticProps
:
module.exports = withMarkdoc({ mode: 'static' })({ pageExtensions: // [...](https://nextjs.org/docs/api-reference/next.config.js/custom-page-extensions) });
Schema customization
You can define your Markdoc schema by creating a /markdoc/
directory at the root of your project. This is where custom nodes, tags, and functions are defined.
. ├── components │ ├── ... │ └── Link.js ├── markdoc │ ├── functions.js │ ├── nodes │ │ ├── ... │ │ ├── link.markdoc.js │ │ └── index.js │ └── tags │ ├── ... │ └── index.js ├── pages │ ├── _app.js │ └── index.md └── next.config.js
You can choose the import location for your schema by passing the schemaPath
option to withMarkdoc
:
module.exports = withMarkdoc({ schemaPath: './path/to/your/markdoc/schema' })({ pageExtensions: // [...](https://nextjs.org/docs/api-reference/next.config.js/custom-page-extensions) });
Tags
You register custom tags by exporting an object from /markdoc/tags.js
(or /markdoc/tags/index.js
). In this example, the tag name is button
. The render
field tells Markdoc to render a Button
React component whenever the {% button %}
tag is used.
// markdoc/tags.js import { Button } from '../components/Button'; export const button = { render: Button, attributes: { href: { type: String } } };
If you want to use kebab case for your tag names, you can export an object like:
// markdoc/tags.js export default { 'special-button': { render: SpecialButton, attributes: { href: { type: String } } } };
Nodes
Custom node registrations are almost identical to tags, except you create a /markdoc/nodes.js
file instead, for example:
// markdoc/nodes.js import { Link } from 'next/link'; export const link = { render: Link, attributes: { href: { type: String } } };
This example overrides the default link
node.
Functions
Custom functions registrations are almost identical to tags and nodes, except you create a /markdoc/functions.js
file instead, for example:
// markdoc/functions.js export const upper = { transform(parameters) { const string = parameters[0]; return typeof string === 'string' ? string.toUpperCase() : string; } };
Advanced
If you want more control over your config object, or you are using the Markdoc language server for Visual Studio Code, you can create a /markdoc/config.js
file and export the full config object. This allows you to extend your config with more data, like records or utility functions.
// markdoc/config.js import tags from './tags'; import nodes from './nodes'; import functions from './functions'; export default { tags, nodes, functions // add other stuff here };
Frontmatter
Markdoc is frontmatter agnostic, however, @markdoc/next.js
uses YAML as its frontmatter language. You can access the frontmatter object within your _app.js
under pageProps.markdoc.frontmatter
, or in your content using the $markdoc.frontmatter
variable.
For example:
--- title: Using the Next.js plugin description: Integrate Markdoc into your Next.js app --- # {% $markdoc.frontmatter.title %}
Partials
Partials automatically load from the /markdoc/partials/
directory. For example:
{% partial file="header.md" /%}
would load and render the contents of markdoc/partials/header.md
Layouts
To create a custom layout for each of your Markdown/Markdoc files, wrap your Component
within your _app.js
, for example:
// pages/_app.js import Layout from '../components/Layout'; export default function App({ Component, pageProps }) { return ( <Layout frontmatter={pageProps.markdoc.frontmatter}> <Component {...pageProps} /> </Layout> ); }
Comments
You can add comments to your Next.js content by passing allowComments
to the Markdoc tokenizer:
const withMarkdoc = require('@markdoc/next.js'); withMarkdoc({ tokenizerOptions: { allowComments: true } });
and then using comment syntax:
<!-- Your comment here -->
Built-in Next.js tags
Next.js Markdoc provides custom tags out-of-the-box that you can add to your schema. To include them, export them by name in your schema directory (for example, /markdoc/
). For example:
// markdoc/tags/Next.markdoc.js export { comment, head, link, script } from '@markdoc/next.js/tags'; // or export * from '@markdoc/next.js/tags';
Head
Renders a Next.js Head
component. You can use this to add stuff to the <head>
of your page.
You need to create and register your own tags for meta
, title
, and so on.
{% head %} Add custom `title` and `meta` tags here… {% /head %}
Link
Renders a Next.js Link
component. Requires passing an href
attribute.
{% link href="/docs/getting-started" %} Getting started {% /link %}
Script
Renders a Next.js Script
component. Requires passing a src
attribute.
{% script src="https://js.stripe.com/v3" /%}