On This Page
Markdown
For authoring in markdown, Greenwood provides a plugin that you can install which by default supports the CommonMark (opens in a new window) specification and uses unifiedjs (opens in a new window) as the markdown / content framework. See the plugin's README (opens in a new window) for additional information, like standalone usage.
Installation
You can use your favorite JavaScript package manager to install this plugin:
npm i -D @greenwood/plugin-markdown
yarn add @greenwood/plugin-markdown --dev
pnpm add -D @greenwood/plugin-markdown
Then add this plugin to your greenwood.config.js.
import { greenwoodPluginMarkdown } from "@greenwood/plugin-markdown";
export default {
plugins: [greenwoodPluginMarkdown()],
};
Usage
Now you can start authoring your pages in markdown:
src/
pages/
blog/
first-post.md
second-post.md
index.md
Types
Types should automatically be inferred through this package's exports map, but can be referenced explicitly in both JavaScript (JSDoc) and TypeScript files if needed.
/** @type {import('@greenwood/plugin-markdown').MarkdownPlugin} */
import type { MarkdownPlugin } from '@greenwood/plugin-markdown';
Options
Plugins
You can install remark or rehype compatible plugins to extend this plugin's markdown rendering and transformation capabilities by passing their names in as an array.
For example, after installing something like rehype-slug, pass the name as a string when adding the plugin to your Greenwood config file:
import { greenwoodPluginMarkdown } from '@greenwood/plugin-markdown';
export default {
plugins: [
greenwoodPluginMarkdown({
// npm i -D rehype-slug
plugins: [
"rehype-slug"
],
})
]
}
If you need to pass options to a markdown plugin, you can use object syntax with the plugin name and the options it takes.
import { greenwoodPluginMarkdown } from '@greenwood/plugin-markdown';
export default {
plugins: [
greenwoodPluginMarkdown({
plugins: [
"rehype-slug",
{
name: "rehype-autolink-headings",
options: {
behavior: "append"
},
},
],
})
]
}
Syntax Highlighting
Although Greenwood does not provide any syntax highlighting by default, you can add support for Prism (opens in a new window), for example.
Just install @mapbox/rehype-prism
via npm and pass it as a markdown plugin in your configuration file:
export default {
markdown: {
plugins: ["@mapbox/rehype-prism"],
},
};
And then include a Prism theme (opens in a new window) from a CSS file in your project:
@import url("../node_modules/prismjs/themes/prism-tomorrow.css");
Then if you add one of the supported language (opens in a new window) after the fencing prismjs will add syntax highlighting to your code fences.
Write the following in your markdown
```js
const hello = "world";
console.log(hello);
```
To get this result:
const hello = "world";
console.log(hello);
Table of Contents
This plugin supports the addition of a frontmatter property called tocHeading
that will read all the HTML heading tags that match that number in your markdown, and provide that as a subset of the data object in your pages data schema. This is most useful for generating the table of contents for a page.
For example:
---
author: Project Evergreen
published: 2024-01-01
tocHeading: 2
---
# First Post
This is my first post.
## Overview
Lorum Ipsum
## First Point
Something something...
We would get this additional content as data:
{
"id": "blog-first-post",
"title": "First Post",
"label": "First Post",
"route": "/blog/first-post/",
"data": {
"author": "Project Evergreen",
"published": "2024-01-01",
"tocHeading": 2,
"tableOfContents": [
{
"content": "Overview",
"slug": "overview"
},
{
"content": "First Point",
"slug": "first-point"
}
]
}
}
Active Frontmatter
With activeContent
enabled, any of these properties would be available in your HTML or markdown through Greenwood's content as data features.
---
author: Project Evergreen
---
# My Post
Authored By: ${globalThis.page.data.author}
Web Components
Web Components work great with markdown, and can be used to mix markdown authored content as HTML to be "projected" into a custom element definition. We make extensive use of the HTML Web Components pattern in this documentation site, which allows us to encapsulate styles and layout around generic, page specific content; for example to create our "section headers" (opens in a new window).
Perfect for documentation sites when combined with prerendering and static optimization configuration options, to get zero runtime templating. Works great with syntax highlighting too! ✨
The example below mixes static content and attributes, leveraging our (optional) CSS Modules plugin:
import styles from "./heading-box.module.css";
export default class HeadingBox extends HTMLElement {
connectedCallback() {
const heading = this.getAttribute("heading");
this.innerHTML = `
<div class="${styles.container}">
<h1 class="${styles.heading}" role="heading">${heading}</h1>
<div class="${styles.slotted}" role="details">
${this.innerHTML}
</div>
</div>
`;
}
}
customElements.define("app-heading-box", HeadingBox);
And then anywhere in our pages we can use it with any custom content:
<app-heading-box heading="Plugins">
<p>Some content about plugins.</p>
</app-heading-box>
This would produce the following HTML output:
<app-heading-box heading="Plugins">
<div class="heading-box-1843513151-container">
<h1 class="heading-box-1843513151-heading" role="heading">Plugins</h1>
<div class="heading-box-1843513151-slotted" role="details">
<p>Some content about plugins.</p>
</div>
</div>
</app-heading-box>
There are some known issues and conventions around mixing Web Components and markdown to be aware of that we cover in this discussion (opens in a new window).