Usage guide
← Docs overviewThe Metalsmith directory
The Metalsmith constructor takes a working directory as single argument (in 99% of cases the parent directory of metalsmith.js
). With ES modules there are 2 extra lines of code.
If you are using the CLI with a metalsmith.json
config file, there is no need to specify metalsmith.directory
explicitly, it will default to __dirname
.
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!
*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:
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
Plugins can be broadly divided into a few categories:esbuildesbuild
- 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: @metalsmith/excerpts, @metalsmith/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/js-bundle, 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:
And run it with:
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:
If you need to check multiple conditions at different places in the build, the metalsmith-if plugin might be a better match:
Defining metadata
You can define global metadata for a Metalsmith build using the metalsmith.metadata
method:
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:
...that we can then refer to in the build like:
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:
Rendering content
There are a multitude of plugins which can be used to render content. For rendering markdown contents and file metadata keys, there is @metalsmith/markdown. @metalsmith/layouts combined with a jstransformer wraps content in layouts, and @metalsmith/in-place is useful if you need to use a templating language within a file's contents (for example within markdown files).
There are also other rendering plugins like metalsmith-twig or metalsmith-handlebars-x that provide full integrations for specific templating languages.
Using the Metalsmith environment
Since version 2.5.0, Metalsmith has its own Metalsmith.env
method. Metalsmith plugins can read and use conventional variables to set more sensible defaults. A few notable conventions are NODE_ENV
, DEBUG
and TZ
:
The @metalsmith/sass plugin for example will output source maps and skip minifying the resulting CSS if metalsmith.env('NODE_ENV') === 'development'
(to minimize build time and maximize ability to debug).
Debugging
Most Metalsmith plugins use the debug package for logging and debugging. You can enable targeted or global debugging to get a better idea of what your metalsmith plugin chain is doing. Since version 2.5.0, debugging in metalsmith can be enabled by passing a debug value to metalsmith.env
like so:
The previous example sets DEBUG: true
which is the same as the globstar wildcard *
, meaning debug all. If you wanted to debug a specific plugin, say @metalsmith/markdown
, you would set metalsmith.env('DEBUG', '@metalsmith/markdown*')
.
Using the DEBUG environment variable
Older plugins released prior to Metalsmith 2.5.0 often use the debug package directly: these can only be controlled by the DEBUG
(operating system-level) environment variable. To get those logs to conform the best solution is to pass process.env.DEBUG
to metalsmith:
You can choose the DEBUG value every time you run a metalsmith build, for example:
To avoid having to mess with Mac/Linux vs Windows syntax, use the cross-env NPM package: cross-env DEBUG=* node metalsmith.js
Debug values
The list below shows the different types of values you could choose to pass to debug:
false
,''
: debug offtrue
,*
: debug all (including dependencies used by metalsmith plugins!)@metalsmith/*
: debug core plugins onlymetalsmith-*
: debug third-party plugins onlymetalsmith-<pluginName>*
: debug all channels of a specific third-party plugin@metalsmith/*,metalsmith-*
: debug all metalsmith plugins@metalsmith/*:warn
: debug only the warnings channel of metalsmith core plugins
Storing debug logs in a file
You can choose to store debug logs in a file instead of logging them to the console by specifying metalsmith.env('DEBUG_LOG', 'path/relative/to/ms/dir.log')
. Note that this will affect only plugins using metalsmith.debug
.
The log can only be output either to console, or a log file. Therefore enabling DEBUG_LOG
is more suitable for server environments with file system persistence, or if you want to git version the build log or store it as a CI artifact.
Adding your own debug logs
You can use metalsmith.debug
for your own build logs as well. The method returns a debugger with 3 channels with their own colors: info (cyan), warn (orange), and error (red). Make sure to enable DEBUG
through metalsmith.env
before logging your first log. Run the example below with DEBUG=build* node metalsmith.js
(prefix with SET
for Windows):
Placeholders like %O
(object, multi-line) and %s
(string) can be used as in the example, see debug formatters. The metalsmith debugger also adds a %b
formatter for Node buffers, which is ideal for logging file contents
: it will log the first 250 characters of text files followed by ...
not to clutter your console. Happy debugging!
You can enable the metalsmith debugger to log outside the metalsmith build by running metalsmith.debug.enable('*')
first.
Development setup
Auto-rebuild and browser live reload
Web developers have grown accustomed to the ease of development making a change in the source code and have their builds update automatically and the browser reload. Though Metalsmith does not (yet) offer partial rebuilds, you can quite easily set up an automatic rebuild using a file watcher library (recommended: chokidar) and a browser synchronization package (recommended: browsersync).
In order to do so wrap your Metalsmith build in a function:
The last few lines detect whether the file was executed as entry point by Node (node metalsmith.js
-> isMainScript
) or it was require
d or import
ed from another file. Now let's create a second script dev.js
which will be the entry point for local development with file watching and browser sync:
That's all! You can also check out the full setup of this example on Replit: