Usage guide← Docs overview
The 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
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
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!
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
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 as
draft: trueto 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 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 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:
You can define global metadata for a Metalsmith build using the
Global metadata can be dynamically added from files in
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
--- 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:
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
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).
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
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
The list below shows the different types of values you could choose to pass to debug:
'': debug off
*: debug all (including dependencies used by metalsmith plugins!)
@metalsmith/*: debug core plugins only
metalsmith-*: debug third-party plugins only
metalsmith-<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
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
metalsmith.env before logging your first log. Run the example below with
DEBUG=build* node metalsmith.js (prefix with
SET for Windows):
%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
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
imported 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:
Run it on replit.com