I am using gatsby with plugins gatsby-source-filesystem
and gatsby-transformer-remark
to display markdown files as pages, as described in the official docs.
It works great, but I am looking for a way to add default classes to all the elements that are converted from markdown.
Let's say I want each <h1>
element to have a class of title
, and <h2>
elements to have a class of subtitle
by default.
I managed to do something like this with gatsby-remark-attr
, but with that I can only add classes programmatically in the markdown file. It looks like this:
# My markdown heading
{.title}
## Subtitle
{.subtitle}
converts to
<h1 class="title">My markdown heading</h1>
<h2 class="subtitle">Subtitle</h2>
I am looking for a way to define the default classes once for each element and have them applied automatically, without having to specify them in the markdown files.
TL,DR: Use gatsby-remark-default-html-attrs
Gatsby's gatsby-transformer-remark
use mdast-util-to-hast
to convert markdown nodes to html nodes, which then stringified into raw HTML. If the markdown node has a data.hProperties
object, it'll be converted into html attributes.
Let's say you want to add class name foo
to all h1
nodes. You'd need to:
h1
html elementdata.hProperties
First, you need a custom plugin to modify markdown nodes of transformer-remark
. Thankfully, creating a local plugin with gatsby is trivial:
# Create a `plugins` folder at your root
mkdir plugins
mkdir plugins/remark-default-class-name
cd plugins/remark-default-class-name
npm init -y
touch index.js
You'll now get this structure:
root
|--src
|--gatsby-config.js
`--plugins
`--remark-default-class-name
|--package.json
`--index.js
Then add the new local plugin to gatsby-config.js
:
// gatsby-config.js
module.exports = {
plugins: [
{
resolve: `gatsby-transformer-remark`,
options: {
plugins: [
+ `remark-default-class-name`
],
},
},
The plugin will be given a markdownAST
object, which allows you to find & modify nodes.
I'd use unist-util-select
to help finding the right node. It comes with gatsby-transformer-remark
, but if for some reasons it doesn't work, just install it again.
From here on, it's trivial to find the node:
const { selectAll } = require('unist-util-select');
module.exports = ({ markdownAST }) => {
// `heading` is equivalent to `h1...h6` in markdown.
// specify [depth] allow us to target the right heading tag.
const h1Nodes = selectAll('heading[depth=1]', markdownAST);
console.log(h1Nodes)
// this yields
// [{ type: "heading", children: [{ type: "text", value: "..." }] }, ...]
}
We can modify the node directly.
const h1Nodes = selectAll('heading[depth=1]', markdownAST);
- console.log(h1Nodes)
// node doesn't always have data
+ if (!node.data) node.data = {};
+ node.data.hProperties = {
+ className: 'foo'
+ }
That's it, all h1
should have a foo
class now.
This is a particular interesting question for me, since I'm learning about Unist and its ecosystem, which powers remark
; so thanks for that.
I make a simple plugin that's a bit more generic here, feel free to try it out & let me know if something failed.
There's also gatsby-remark-classes
(GitHub, NPM) which allows you to specify a classMap
in your gatsby-config.js
.
{
resolve: `gatsby-transformer-remark`,
options: {
plugins: [
{
resolve: `gatsby-remark-classes`,
options: {
classMap: {
"heading[depth=1]": "title",
"heading[depth=2]": "subtitle",
paragraph: "para",
}
}
}
]
}
}
Its functionality seems identical to gatsby-remark-default-html-attrs
. Funnily enough, these two plugins were created within one day of each other (Feb 1, 2019 and Feb 2, 2019, respectively).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With