Miss any of our Open RFC calls?Watch the recordings here! »


1.26.2 • Public • Published

plaf is a markdown static website generator. It only expects you to write documents in markdown, with minimal profile. Use your own folder structure, your own file names, your own style with handlebars templates. No config files. No conventions.

How does it work?

Step 0.: Install plaf (you need nodejs for that.)

npm i -g plaf

Step 1. Go to the folder containing markdown files.

cd ~/my-folder

Step 2. Run plaf


Step 3. Success. You static website is in the rendered directory.

What else can I do?

Specify which folder to render, and where to output

Use --in (or -i) to specify the input folder.


Use plaf as server

If you want to navigate your content directly without generating a folder, plaf can act as a server. Use option --serve and connect to http://localhost:8080 (or use option --port 1234 to specify which port to use ; 8080 is default)

Use templates

Use your own templates using handlebars syntax. The content goes into a content variable. The title goes into a title variable. This is an example of what a template looks like:

    <div class="site-title">

To use a template, specify the --template (or -t) option to plaf, and point to the handlebars file.

Your own variables

Plaf supports yaml headers on your markdown files. Add this to your markdown file:

title: This is the title
category: bla
This is the body

By default it's using your file names as title. You can override that with the headers. You can add all the headers you want, and use them as variables in your templates. Plaf also sends its internal values to the template, if you want to use them, including:

  • title
  • type: md for markdown files, folder for folders.
  • content and mdcontent: the content.
  • path, relativePath: files paths
  • tags: a list of tags for md files
  • files: a list of files contained in the folder or tag. These files have the same properties.

Customize templates per file

If you add a template property to your markdown headers, and set the value to the path of the file you want to use as a template.

Create a template library

Create a .plaf folder at the root of your markdown folder, and add template files to this folder. Then, from the markdown files, you can refer to these using only their names rather than their path. Plaf uses the .handlebars as default for the templates, so if you have a .plaf/blog-post.handlebars template file, then your header can be:

title: This is a blog post
template: blog-post

You can point to another template library by using the --templates (or -T) option.

Plaf uses default.handlebars by default, and index.handlebars for indexes, and tags.handlebars for tags index.

Use helpers in templates

plaf uses handlebars for generation. It comes with the following helpers: ne, eq, ge, lt, le, gt. This means that you can use the following syntax in your template:

{{#if eq someproperty "true"}}

You can add you custom helpers in the .plaf/helpers folder at the root of the directory you're processing. Plaf will use the file name, minus the js extension, as the helper name. The helper content must be a javascript function.

Create your custom index

If you don't want to use a list of the markdown files in the directory but use your own index instead, create an index.md or a index.html file in the directory, and plaf will use that instead.

Copy resource files

If you create a .plaf/resources directory, plaf will copy everything from it to the render directory, and will not index it. This is useful if you want to include things like fonts or CSS.

Configure sub folders

Add a .plaf file in a folder. Give it properties the same way you would a file. This way, you can specify a custom template for a given index.

Use Markdown extensions

If you use #tags in your md files, then plaf will use them and generate a tags index.

Plaf will also convert --> to → and ==> to ⇒ (and same for left). You can also try <=>, -- and ---, << and >>. To deactivate special characters processing, add specialCharacters: false in the front-matter or use command --no-markdown-extensions

It also handle footnotes in this format^1.

It also handles references like this ([ref](some link)) and will make them appear as superscriptref

It processes wikilinks, such as [[an article file name]] or [[an article file name|label for the link]]. This will resolve to any markdown file in the current structure with a similar file name. It is case insensitive, and ignores spaces, dashed and underscores.

If you don't want plaf to mess with your markdown, use the --no-markdown-extensions (or -M for short) command.

Encrypt content

Markdown content can be encrypted using AES and your password. To do so, either specify option --password when running plaf, or specify a password property in the front matter. Your content gets encrypted using AES, and decrypted on the client side if the right password is provided.

The interesting thing is that since the content gets replaced by an HTML widget that decrypts itself, you can use the content of multiple encrypted files in an index page and still get it protected but readable if you have the password.

Build a client-side search index

Plaf uses Lunr to build a search catalog. If you use --generate-search it will build three files:

  • /search/index.html which is a basic search page
  • /search/catalog.js which contains the search catalog as json (in const catalog=).
  • /search/search.js which contains the search libs.

You can customize the search page by adding a search.md file in a search directory in your folder using these. For example:

title: search

<div class="search-bar" id="loading">Loading search catalog...</div>

<script src="catalog.js"></script>
<script src="search.js"></script>
  function s(evt) {
    const searchResultsComponent = document.getElementById("search-results")
    const res = search(evt.value)
    searchResultsComponent.innerHTML = "";
      .map(result => {
        const elt = document.createElement("li");
        elt.innerHTML = `<a href="/${result.relativePath.replace(/\.md$/, ".html")}">${result.title}</a>`
        return elt;
      .forEach(elt => searchResultsComponent.appendChild(elt));
  loading = document.getElementById("loading").remove();

<input type="text" class="search-bar" id="search-bar" onkeyup="s(this)">
    <ul id="search-results" class="post-list"></ul>


Folders and files can be skipped from indexing by adding a noSearch property in their config files or front matter.


  • generate search page
  • serve dynamically
  • Encrypt content
  • Cache password

What's new


  • Add --serve which dynamically generates the HTML content and serves it from memory.


  • Add unique id to encrypted content to allow multiple encrypted blobs per page


  • Encrypt content


  • Render search page if none is existing


Add support for WikiLinks.




npm i plaf

DownloadsWeekly Downloads






Unpacked Size

267 kB

Total Files


Last publish


  • avatar