Usage guide
Using plugins
A metalsmith plugin is just a function that is passed the Files object and the Metalsmith instance. In fact, we can even use console.log
as a plugin!
metalsmith.js
const Metalsmith = require('metalsmith')
Metalsmith(__dirname)
.use(console.log)
.build((err, files) => {
if (err) throw err
console.log('Build success!')
})
metalsmith.json
{
"plugins": [
{"./plugin-console-log": true}
]
}
*Note that for this example to work if you are using the CLI, you need to create a file plugin-console-log.js
with the contents module.exports = (...args) => console.log(...args)
and reference it as a local plugin in metalsmith.json
This is super-convenient when you want to quickly have a look at all the files and the metalsmith instance. You could use this little plugin to inspect how the file metadata and the metalsmith instance change in-between plugins:
metalsmith.js
const Metalsmith = require('metalsmith')
const drafts = require('@metalsmith/drafts')
const markdown = require('@metalsmith/markdown')
const layouts = require('@metalsmith/layouts')
Metalsmith(__dirname)
.use(console.log)
.use(drafts())
.use(console.log)
.use(markdown())
.use(console.log)
.use(layouts())
.use(console.log)
.build((err, files) => {
if (err) throw err
console.log('Build success!')
})
metalsmith.json
{
"plugins": [
{"./plugin-console-log": true},
{"@metalsmith/drafts": {}},
{"./plugin-console-log": true},
{"@metalsmith/markdown": {}},
{"./plugin-console-log": true},
{"@metalsmith/layouts": { "pattern": "**/*.html" }},
{"./plugin-console-log": true},
]
}
This example also demonstrates that you can re-use the same plugin multiple times across the plugin chain, each time with different input.
Logging is cool, but what about actually manipulating the files? Say, defining additional metadata, rendering markdown files, wrapping them in a layout, adding SASS stylesheets, and optimizing everything for production. Just as the Apple iPhone’s famous 2009 commercial “There’s an app for that”, the answer to how can I do X with Metalsmith? is - there’s a plugin for that. Browse the official plugin registry for inspiration!
Plugin types
There is no official plugin type classification, but plugins can be broadly divided into a few categories:
- Development plugins: plugins that provide a better developer experience or debug information. Examples are: metalsmith-express, metalsmith-writemetadata, metalsmith-debug-ui
- Metadata plugins: plugins that add or modify file and global metadata. Examples are: [core_plugin_excerpts][@metalsmith/excerpts], [@metalsmith/table-of-contents][core_plugin_table-of-contents], @metalsmith/default-values
- Rendering plugins: plugins that render or alter a file’s
contents
. Examples are: @metalsmith/layouts, @metalsmith/in-place, @metalsmith/markdown - Files-tree manipulating plugins: plugins that add, move or remove files from the files object. Examples are: @metalsmith/permalinks, @metalsmith/remove, @metalsmith/drafts, metalsmith-sitemap
- Third-party integrations: plugins that hook third-party tools into the metalsmith build. Examples are @metalsmith/sass, @metalsmith/postcss metalsmith-uglify
A plugin could fit into multiple categories:
Plugins that start with the @metalsmith/
prefix are core plugins. They are officially supported by Metalsmith and there’s a good chance that you will need most of them when building a static site. Here are some of the most common ones:
@metalsmith/sass
: use sass or scss files for styling@metalsmith/drafts
: mark files asdraft: true
to preview them in development mode, but remove them in production@metalsmith/markdown
: convert markdown files & metadata keys to html@metalsmith/collections
: group files by frontmatter key or pattern into collections@metalsmith/layouts
: wrap files in layouts in the templating language of your choice (a.o. Pug, Nunjucks, Handlebars, Twig, Ejs)
Plugin order
Plugin order is very important in Metalsmith. As a rule of thumb, .use(common sense)
: you only want to minify HTML after the markdown file has been processed with @metalsmith/markdown
and then wrapped in @metalsmith/layouts
. Generally, you want plugins that inject new files or add metadata to be run at the start of the plugin chain so it is available in layouts and for other plugins to process. @metalsmith/drafts
is efficient as the first plugin because in a production build it immediately removes the files you don’t want to process anyway.
Conditionally running plugins
If you’re using the Metalsmith CLI, there’s only one way to run plugins conditionally: create multiple metalsmith.json
configs. The common use case is having a development config and a production config. For example, we would like to remove draft files and minify the HTML only in production:
metalsmith.dev.json
{
"plugins": [
{ "@metalsmith/markdown": { } },
{ "@metalsmith/layouts": { } }
]
}
metalsmith.json
{
"plugins": [
{ "@metalsmith/markdown": { } },
{ "@metalsmith/drafts": { } },
{ "@metalsmith/layouts": { } },
{ "metalsmith-html-minifier": { } }
]
}
And run it with:
metalsmith --config metalsmith.dev.json
metalsmith --config metalsmith.json
If you have more than 2-3 conditions we recommend using the JS API. You can run a plugin conditionally by assigning the metalsmith build to a variable, and using native javascript if
statements. The same example from above using the JS API:
metalsmith.js
const Metalsmith = require('metalsmith');
const minifyHTML = require('metalsmith-html-minifier');
const isProduction = process.env.NODE_ENV !== 'development';
const metalsmith = Metalsmith(__dirname);
if (isProduction) {
metalsmith.use(minifyHTML());
}
metalsmith.build(err => {
if (err) throw err
console.log('Build success!')
})
If you need to check multiple conditions at different places in the build, the metalsmith-if plugin might be a better match:
metalsmith.js
const when = require('metalsmith-if');
const minifyHTML = require('metalsmith-html-minifier');
const production = process.env.NODE_ENV !== 'development';
Metalsmith(__dirname)
.use(when(production, minifyHTML())
.build(err => {
if (err) throw err
console.log('Build success!')
})
Defining metadata
You can define global metadata for a Metalsmith build using the metalsmith.metadata
method:
metalsmith.js
const Metalsmith = require('metalsmith')
Metalsmith(__dirname)
.metadata({
sitename: 'My Static Site & Blog',
description: 'It\'s about saying »Hello« to the World.',
generator: 'Metalsmith',
url: 'https://metalsmith.io/'
})
metalsmith.json
{
"metadata": {
"sitename": "My Static Site & Blog",
"description": "It's about saying »Hello« to the World.",
"generator": "Metalsmith",
"url": "https://metalsmith.io/"
}
}
Global metadata can be dynamically added from files in metalsmith.directory
or metalsmith.source
with a plugin like @metalsmith/metadata
, which can help keep your main build file clean. Here is the same metadata in a separate yaml file:
src/site.yaml
sitename: My Static Site & Blog
description: It's about saying »Hello« to the World.
generator: Metalsmith
url: 'https://metalsmith.io/'
…that we can then refer to in the build like:
metalsmith.js
const Metalsmith = require('metalsmith')
const metadata = require('@metalsmith/metadata')
Metalsmith(__dirname)
.use(metadata({
site: 'src/site.yaml'
}))
metalsmith.json
{
"plugins": [
{ "@metalsmith/metadata": { "site": "src/site.yaml" } }
]
}
File metadata can be defined as front-matter in any file (provided that you didn’t disable it with metalsmith.frontmatter(false)
):
---
title: My first post went a little like this
description: A turn, an ending, and a twist
---
No more drafts and no more waiting
File metadata can be added dynamicaly with plugins like @metalsmith/default-values or metalsmith-filemetadata. Below is an example using @metalsmith/default-values
to automatically assign the post.hbs
layout to files by folder and mark all files in the drafts
folder as draft:
metalsmith.js
const Metalsmith = require('metalsmith')
const defaultValues = require('@metalsmith/default-values')
const drafts = require('@metalsmith/drafts')
const markdown = require('@metalsmith/markdown')
const layouts = require('@metalsmith/layouts')
Metalsmith(__dirname)
.use(defaultValues([
{
pattern: 'posts/**/*.md',
defaults: {
layout: 'post.hbs'
}
},
{
pattern: 'drafts/**',
defaults: { draft: true }
}
]))
.use(process.env.NODE_ENV === 'production' ? drafts() : () => {})
.use(markdown())
.use(layouts())
.build((err, files) => {
if (err) throw err
console.log('Build success')
})
metalsmith.dev.json
{
"plugins": [
{
"@metalsmith/default-values": [
{ "pattern": "posts/**/*.md", defaults: { "layout": "post.hbs" }},
{ "pattern": "drafts/**", defaults: { "draft": true }},
]
},
{ "@metalsmith/drafts": {} },
{ "@metalsmith/markdown": {} },
{ "@metalsmith/layouts": {} }
]
}