Getting started
← Docs overviewIntroduction
What is Metalsmith?
Metalsmith is an extremely simple, pluggable static-site generator for NodeJS. Let us break that down:
Metalsmith is a static site generator
The task of a static site generator is to produce static files that can be deployed to a web server. For a static site generator this means:
- take a source directory, read the source files and extract their information
- manipulate the information with plugins
- write the manipulated information to files into a destination directory (NPM is )
Metalsmith is built on this reasoning. It takes the information from the files in a source directory and it writes the manipulated information to files into a destination directory.
Metalsmith is extremely simple
- Plain old javascript objects: the source directory is initially converted into a Files object whose keys are file paths, and values are javascript objects with the usual
{key:value}
pairs. These{key:value}
pairs contain information about the original file (such as itsmode
orstats
) and itscontent
.The File object for each file is then supplemented with all variables specified in the front-matter of the file or supplied via plugins. - Everything is a plugin: all functionality is exclusively handled by plugins. Metalsmith itself only provides a streamlined interface that the plugins can use to apply those manipulations. And they can do most of it conveniently by altering the file objects' keys and values. A plugin is just a function that receives a Files object, applies its manipulations and calls done(), — you guessed it —, when they're done. Anyone who can write a javascript function can write a metalsmith plugin.
- Single dependency: Metalsmith is completely in plain javascript and only requires Node. No need to install Ruby, Rust or Go. No need to be familiar with layers of opinionated abstractions like React or Vue. Start small and add extra plugins over time.
Metalsmith is pluggable
Metalsmith leaves all manipulations exclusively to plugins.
Manipulations could be anything: translating templates, transpiling code, replacing variables, wrapping layouts around content, grouping files, moving and removing files and so on. This is why we say Everything is a Plugin. Manipulations can be applied one after another, or chained or piped, if you prefer. Obviously, in a chain plugin order matters.
Splitting metalsmith in a minimal, rock-solid core and multiple plugins reduces complexity. It gives you the freedom to use exactly and only those plugins you need and it distributes the honor and the burden of maintaining the Metalsmith ecosystem.
Metalsmith is more
Depending on your desired use-case, Metalsmith can be much more than a static site generator:
- a deploy, backup or build tool
- a file metadata extractor
- a boilerplate scaffolder
- a continuous integration pipeline
- a lightweight filesystem abstraction for everything else
Thanks to its simple in-memory abstraction, metalsmith builds and plugins are easy to unit-test without the need for extensive mocks.
Have a look at the Metalsmith tests to get an idea.
Prerequisites
Metalsmith runs on all NodeJS Long-Term Support versions that are not end-of-life and some more. Download & install NodeJs and NPM (NPM comes pre-bundled with Node): https://nodejs.org/en/download/.
If you plan to publish plugins, it is recommended that you use nvm or nvs (for Windows)
Add node_modules/.bin
to the PATH
environment variable if you'd like to use the Metalsmith CLI:
- on Windows, hit the Windows key and search for Edit environment variables, then add a
node_modules\\.bin
line to thePATH
variable - on Linux/Mac, add
export PATH=node_modules/.bin;$PATH
to your~/.bashrc
or~/.bash_profile
file, thensource
it.
Metalsmith builds are tested on both Linux & Windows systems and all officially supported NodeJS LTS releases.
Installation
First create a project folder somewhere and navigate to it:
Then install Metalsmith with any NodeJS package manager:
It is likely that you will want to install some plugins too:
If you would like to use Typescript in your build file, be sure to install @types/metalsmith
as well.
Concepts
There are only a few core concepts you need to be familiar with to understand how to work with Metalsmith, and if you've worked with another static site generator or run some commands in a terminal like git add
or ls -la
, you are probably already familiar with most of them.
Files
Everyone has worked with files before. They have a name, a path, an extension, contents and metadata (like the last modified date). Metalsmith represents every file in the source directory as a javascript File object. For instance,
src/post/my-file.md:
---
title: A Catchy Title
draft: false
---
An unfinished article...
...becomes
{
'post/my-file.md': {
title: 'A Catchy Title',
draft: false,
contents: 'An unfinished article...',
mode: '0664',
stats: {
/* keys with information on the file */
}
}
}
...where the content of the file is always mapped to the property value of contents
. For illustration purposes only we display the value of contents
as a string. Technically, the property value of contents
is mapped as a NodeJS Buffer
, which can also handle binary data (for images, PDF's, etc). mode
contains the file permission bit and stats
has more technical information on the file such as size
or birthtime
. The file is also parsed for YAML front matter, which is merged into the File object. Thus, we finally have a javascript Files object of objects.
{
'relative_to_sourcepath/file1.md': {
title: 'A Catchy Title',
draft: false,
contents: 'An unfinished article...',
mode: '0664',
stats: {
/* keys with information on the file */
}
},
'relative_to_sourcepath/file2.md': {
title: 'An Even Better Title',
draft: false,
contents: 'One more unfinished article...',
mode: '0664',
stats: {
/* keys with information on the file */
}
}
}
Plugins can then manipulate the javascript File objects representing the original files however they want, and writing a plugin is super simple.
Front matter
To attach metadata to a JS File object, metalsmith reads front matter. Front matter is a term borrowed from the publishing industry meaning metadata about a written work. In Metalsmith this is a YAML document section (delineated by ---
) containing metadata (matter) at the top (front) of a file (commonly, markdown). Metalsmith will recognize and read the front matter of a file and add it as metadata to the JS file representation when you run the build. Here is a typical example of an index.md
file with YAML front-matter. If you don't like the YAML syntax you can use JSON front matter as well
The front-matter will be parsed by Metalsmith as:
{
title: 'Hello World',
keywords: ['hello','world'],
draft: false,
contents: <Buffer>,
mode: '0644',
stats: { ... }
}
When the front matter is read into javascript, we refer to it as file metadata.
Multi-line strings
A common requirement is to write multi-line strings in YAML, either for readability or for output. There are a lot of ways to write multiline strings in YAML. Examples of the two most common ones are shown here:
Using the greater than sign
>
(removing line breaks)description: > This is a long text string, but we actually only want the line breaks for readability in the source. The line breaks will be removed in the output.
Using the pipe sign
|
(preserving line breaks)description: | This is a long text string. It should read like a poem So we want to preserve the line breaks.
Glob patterns
Metalsmith and its plugins make extensive use of glob patterns to target specific files (usually through the pattern
option). A glob is a type of string pattern syntax that is commonly and conveniently used to match files by path with support for globstar wildcards *
. Chances are you are already using glob patterns in .gitignore
files, with the Linux/Mac or git
terminal commands. Here are a few examples of how you can match with glob patterns:
Matching all files (the double globstar is recursive): see it in action
**/*
Matching all files at the root of a directory with a
.html
extension: see it in action*.html
Matching all markdown files starting with
post-
under theblog/posts
folder: see it in actionblog/posts/post-*.md
Matching either ... or ...: see it in action
{services/**,blog}/index.md
Matching all except markdown files (negated match): see it in action
!**/*.md
You can always use DigitalOcean's handy Glob tool or globster.xyz to test your glob patterns.
The plugin chain
We believe that understanding the internal representation of files as JavaScript objects is really key to fully grasp the concept of Metalsmith. To understand this better, we follow the evolution of a file at each step of the build process (between each use
statement). We are also using the writemetadata()
plugin, which writes the {key: value}
pairs excerpted from the File objects representing the files, to the filesystem as .json
files. You can then view the .json
files to find out how files are represented internally in Metalsmith.
In the example above, after applying .use(markdown())
the initial representation of my-file.md
becomes my-file.html
. The markdown plugin changes the file extension and converts the contents to HTML.
{
'relative_to_sourcepath/my-file.html': {
title: 'A Catchy Title',
draft: false,
contents: '<p>An unfinished article...</p>',
...
}
}
After applying .use(permalinks())
the file is renamed to original-name/index.html
and a path
property is added to the file's metadata:
{
'relative_to_sourcepath/my-file/index.html': {
title: 'A Catchy Title',
draft: false,
contents: '<p>An unfinished article...</p>',
path: 'myfile',
...
}
}
Assuming we have defined a very simple nunjucks layout file in a separate layouts folder...
... after applying .use(layouts())
in our Metalsmith chain our JavaScript object becomes:
{
'relative_to_sourcepath/my-file/index.html': {
title: 'A Catchy Title',
draft: false,
contents: `<!doctype html><html><head>
<title>A Catchy Title</title></head><body>
<p>An unfinished article...</p>
</body></html>`,
path: 'myfile',
...
}
}
Finally when the .build(function(err))
is performed our JavaScript object is written to relative_to_destpath/myfile/index.html
.
Quickstart
You want to build a website or blog with a static site generator. Well, here is our elevator pitch. It's as easy as that:
Directory structure
A typical directory structure for a metalsmith (static-site) project looks more or less like this:
repo
├── metalsmith.js
├── package.json
├── node_modules
├── build
├── layouts
│ ├── default.hbs
│ └── post.hbs
├── lib
│ └── sass
│ │ └── style.scss
│ ├── data
│ │ └── nav.json
│ └── plugins
│ └── local-metalsmith-plugin.js
└── src
├── about.md
├── index.md
└── posts
├── first-post.md
└── second-post.md
where:
repo
is yourmetalsmith.directory()
— the root of the project, with all dependencies, config files, etc.src
is yourmetalsmith.source()
— it contains the contents that metalsmith needs to process and output tobuild
build
is yourmetalsmith.destination()
— it is created dynamically when you first runmetalsmith.build
lib
is for all the rest (for example local metalsmith plugins, Sass styles that are processed outside of metalsmith and added to the build, or metadata that is manuallyrequire()
'd)
...but Metalsmith gives you total freedom about how you want to structure your project, so feel free to restructure things as you see fit.
Starter projects
The community has built a few interesting starter projects:
- metalsmith/startbootstrap-clean-blog: a stylish, responsive blog theme for Bootstrap created by Start Bootstrap, and made customizable with metalsmith. | Demo
- wernerglinka/metalsmith-bare-bones-starter: Bare-bones metalsmith starter with markdown & Nunjucks templating | Demo
- wernerglinka/metalsmith-blog-starter: Blog metalsmith starter with markdown & Nunjucks templating + a landing page & some sample articles | Demo
- wernerglinka/metalsmith-company-starter: Company site starter with markdown & Nunjucks templating | Demo
- webketje/metalsmith-starter-resume: A professional, responsive Bootstrap4-themed resume / CV, made highly customizable with metalsmith, Handlebars & SCSS. Features print-friendly version, and twitter/ facebook/ linkedin share tags.. | Demo
There is also a one-click Netlify CMS starter.