Lume SSG Handbook How To Create A Static Blog With Lume
Lume SSG Handbook How To Create A Static Blog With Lume
Lume
freecodecamp.org/news/how-to-create-a-static-blog-with-lume
Rajdeep Singh
Lume is a new static site generator based on Deno. Deno is a JavaScript-based
run-time environment that supports TypeScript.
Lume is not built around any specific language. It supports Markdown, Nunjucks,
TypeScript, and JavaScript by default. Lume also supports plugins. Some plugins come
preinstalled by default. This is why Lume itself is template-language agnostic.
Before learning more about Lume, let's discuss Deno and consider some important Deno
features.
What is Deno?
Deno is an alternative to Node.js built by Ryan Dahl (who also developed Node). Deno is
based on the Rust programming language, and the second main component in Deno is the
JavaScript V8 engine for WebAssembly.
Deno has many cool features – it's fast, secure by default, is compatible with web assembly
and has TypeScript support, has in-built development tools, and more. Deno also supports
Node.js APIs so you can use all npm packaged with Deno.
1/28
In Deno, you do not need to create a configuration file to run a simple program. You simply
deploy your website instantly with a second on-edge network. But my final favorite feature is
the new node_modules folder in the workspace. Deno caches all the packages locally and
uses them, which is very fast compared to Node.
You can check out the demo blog website here, and all the code is available on GitHub here.
Table of Contents
Lume + Markdown
Lume is a new static site generator based on Deno created and maintained by Óscar Otero.
Lume uses markdown-it as the default markdown. You can use the remark plugin to change
the default markdown.
Markdown is a language that helps to write documents, readme files, and blogs on the
internet. John Gruber created markdown in 2004.
If you've worked with GitHub and written README files, that means you are likely familiar
with GFM markdown. If you don't like the default (markdown-it) markdown, you can change
the markdown with the remark plugin.
There are tons of static site generators. So why is Lume special? What does it provide
compared to other static site generators? Let's find out.
Lume works similarly to a GitHub readme file. If you're familiar with writing one of those (and
using markdown), you do not need to learn anything else to write articles and documentation
with Lume.
1. Lume supports multiple template engines like Markdown, Nunjucks, Eta, JSX, Liquid,
or Pug.
2. It supports multiple authors
2/28
3. It has code syntax highlighting
4. There's great SEO support
5. Lume supports multiple languages
6. It has Windi CSS support
7. There's pagination and component support
8. It supports minifying JavaScript, HTML, CSS, and SASS
9. It has relations support
10. There is the built-in search functionality
11. It supports Netlify CMS
12. It supports images and SVGs
13. There's Remark.js plugin support
14. You can deploy with Netlify, Vercel, GitLab Pages, and the GitHub page.
With Lume processors and preprocessors, you can easily manipulate the HTML code with
the JavaScript DOM API. Other static site generators support a few template engines, but
Lume supports many template engines like JavaScript, JSX, Nunjucks, Eta, JSX, Liquid, and
Pug.
Note that Lume can seem tough to get started with for beginners. But if you're following my
article, just make sure to open the code which will make things much clearer for you.
3/28
Lume installation demo
4/28
To run a local development server, you'll use the deno task lume --serve command. To
build a website, run the deno task build command.
If you face a 404 - not found error, you can create a index.njk file within the root folder.
---
title: "hello"
---
hello world
index.njk
5/28
Lume hello-world
Additional folders:
1. posts folder is not a compulsory folder. It contains all your posts' markdown files.
2. pages folder is not a compulsory folder. It has all your pages' markdown files.
3. author folder is not a compulsory folder. It has all your author markdown files.
4. _components folder is a compulsory folder. It has all your components.
5. _includes folder is a compulsory folder. It contains your layout and templates for
your site.
6. images folder is not a compulsory folder. It contains all your images.
The posts, pages, authors, and images folders are optional. You can rename these folders
according to your wishes. The _components and _includes folders are mandatory and you
don't rename them.
6/28
In Lume, you can create a data variable, which has access to the entire website by all
template engines.
// Set a variable
site.data("post_per_page", 10);
// Set a function
site.data("randomNumber", function () {
return Math.random();
});
You create posts, pages, and author folders in the root folder. Then, inside every folder, you
write markdown files.
You can access all posts, pages, and authors by file name on the browser:
1. localhost:3000/posts/your-title.html
2. localhost:3000/pages/your-pages.html
3. localhost:3000/author/your-author.html
Suppose you need a demo post, pages, and author markdown for a project or template.
Then, you can use demo-markdown posts for your project. It is free and open source, and
you can create your own template.
The home page is based on pagination, and pagination is based on posts. Lume dynamically
generates the pagination.
In Lume, I chose nunjucks and JavaScript to create my demo website. Nunjucks is the
default template engine. You can easily change the default Nunjucks engine with another
template engine with copy-paste code.
7/28
8/28
home page
Lume provides a JavaScript template function that helps create dynamic web pages. If you
create a home page for the site, you need to create an index.tmpl.js file in your root or src
folder. Lume also supports an src folder to organize your project. In my demo project, I'm not
using the src folder.
The *.tmpl.js is an extension of a JavaScript template that helps create dynamic pages for
websites. It comes pre-installed in Lume with the modules plugin.
For example, the following code creates pagination for your website. But the layout comes
from the _includes folder.
9/28
// index.tmpl.js
page.layout = "layouts/home.njk";
} else {
// Render diffrent layout if it is not home page page "/page/2","/page/3",etc
page.title = "Pagination page"
page.layout = "layouts/home.njk";
}
yield page;
}
index.tmpl.js
ume has a search plugin that helps you search pages. In my demo blog, I search all pages
L
base on the type.
In my all posts folder, all posts are defined in type=article, the author is described in
type=author, and pages are defined in type=page . The search plugin is pre-installed with
Lume.
On index.tmpl.js file, you can get all pages that have the type "article" (type=article )
using the following code: const posts = search.pages("type=article",
"date=desc");. The search.pages("type=article", "date=desc") function only returns
those that have type=article .
10/28
The layouts/base.njk layout file contains an HTML base and includes a header and footer
for the website.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }}</title>
<meta name="description" content="{{ description or site.description }}">
</head>
<body>
{% include "layouts/header.njk" %}
{% include "layouts/footer.njk" %}
</body>
</html>
layouts/base.njk
Inside the {{ content | safe }}, Lume renders other HTML, like cards, articles, home
templates, Tag and category pages, and so on.
I used the for loop on the index.tmp.js file that helps get all the posts and send them to the
layouts/home.njk file and the layouts/home.njk file. You get all posts from the result, and
then pass them to the card.njk template.
---
layout: layouts/base.njk
---
{% include "templates/pagnation.njk" %}
layouts/home.njk
11/28
he templates/card.njk file runs for all blogs and generates HTML for each blog. Your
T
card looks like this:
card.njk
In card.js template, you can access it using {{}} curly brackets. Get the title using
{{post.data.title}} and {{post.data.description}}.
In my demo blog, I'm getting only the first category to show inside the card. So I use a
defined filter _config.ts and use it with | symbols. Inside card.njk we get a zero index or
first value in from categories with the following code: {{ post.data.category | category
}}.
To get the author on card.njk I define the relationship between the article and the author
type, which you can learn about from the docs.
12/28
<div class="container px-6 py-10 mx-auto">
{% if post.data.author.length <= 2 %}
<div class="mx-4">
<a href="{{author.url}}" class="text-sm text-gray-700 dark:text-
gray-200">
{{ author.author_name}}</a>
<p class="text-sm text-gray-500 dark:text-gray-400">
{{author.job}}
</p>
</div>
{% endfor %}
{% else %}
<div class="mx-4">
<a href="{{ post.data.author.url}}" class="text-sm text-gray-700
dark:text-gray-200">
{{ post.data.author.author_name}}</a>
<p class="text-sm text-gray-500 dark:text-gray-400">
{{post.data.author.job}}
</p>
</div>
{% endif %}
</div>
</div>
13/28
</div>
</div>
templates/card.njk
The {{ title }} and {{description}} both show the markdown file title and description.
To show the category, I used a filter to show a single category on the article page and define
the filter on _config.ts file. I also show single and multiple authors with For loop. Every card
has its own post.data.url property, after the user clicks on the read more button user ago
respected the article read page. To show the image, I used {{ post.data.image }}
property. I also show single and multiple authors with For loop on card.njk file.
14/28
---
category:
- Blog
date: 2022-03-20T13:09:24Z
description: Dolor excepteur ad ad fugiat Lorem consectetur velit excepteur duis qui.
image: /images/dice.jpg
tags:
- npm
- npm cli
- npm install command
title: Random blog Title for markdown.
draft: false
author_id: 1
type: article
layout: templates/article.njk
---
Laboris consequat elit ad excepteur. Ipsum duis amet dolore voluptate dolore consequat
ullamco incididunt ullamco. Dolore laborum cupidatat dolor ipsum reprehenderit excepteur
cupidatat dolore.
## First
Cupidatat non amet irure esse quis aute qui enim. Est qui ullamco proident consequat aute
reprehenderit eiusmod nisi. Laboris ullamco fugiat sint occaecat.
## Second
Irure fugiat officia non esse esse irure eu sint commodo quis amet. Dolor culpa non amet elit
adipisicing exercitation ex anim velit ipsum.
## conclusion
Culpa irure eiusmod labore ut proident sit enim laborum nulla voluptate eu. Id tempor velit
cillum pariatur est laboris ipsum ad. Sint nostrud nostrud laboris Lorem consequat tempor
voluptate dolore velit. Commodo elit nulla commodo pariatur. Deserunt ipsum fugiat id ipsum
pariatur cupidatat magna ex. Fugiat aliquip nisi laboris aliquip velit velit id quis eu
reprehenderit excepteur fugiat.
posts/*.md
I created an article in the posts folder under type=article . The author_id defines the
relation between the author and the article.
I used templates/article.njk as the layout for my articles page. You can design yours as
per your requirements. You can design the article title, description, author card, and tags as
well.
15/28
---
layout: layouts/base.njk
---
<article class="container mx-auto p-2">
<div class="flex flex-col">
{% if author %}
<div class="flex flex-row mt-4">
{% if author.length <= 2 %}
<div class="mx-4">
<a href="{{author.url}}" class="text-sm text-gray-700 dark:text-gray-200">
{{ author.author_name}}</a>
<p class="text-sm text-gray-500 dark:text-gray-400">
{{author.job}}
</p>
</div>
{% endfor %}
{% else %}
<div class="mx-4">
<a href="{{ author.url}}" class="text-sm text-gray-700 dark:text-gray-200">
{{ author.author_name}}</a>
<p class="text-sm text-gray-500 dark:text-gray-400">
{{ author.job}}
</p>
</div>
{% endif %}
</div>
{% endif %}
</div>
16/28
<div class="mt-4">
{{ content | safe }}
</div>
</article>
{% if previousPost %}
<ul class="flex flex-row w-full mt-10 justify-between p-4">
{%- if previousPost %}
<li class="w-6/12 text-left">
← Previous: <a href="{{ previousPost.data.url }}" rel="prev">{{ previousPost.data.title
}}</a>
</li>
{% endif %}
{# ==== #}
{# Addding the utteranc Commenting script #}
{# ==== #}
<script src="https://utteranc.es/client.js"
repo="officialrajdeepsingh/Minimalist-blog"
issue-term="pathname"
theme="github-light"
crossorigin="anonymous"
async>
</script>
</div>
templates/article.njk
The layouts/base.njk file is the base file for our blog (which I've already explained). The {{
title }} and {{description}} both show the markdown file title and description.
To show tags on the article page, I used a for loop. I also showed single and multiple authors
with the for loop.
To convert the date into a human-readable format, I used Lume date plugin and wrapped it
with a date filter that looks like this: {{ date | date('HUMAN_DATE') }}. To show all
markdown paragraphs, I used {{ content | safe }} .
17/28
For pagination, I used the Lume pagination plugin, and with the search.previousPage(url,
"type=article") function, I showed the next and previous posts on the article page. For
comments, I used utteranc.es.
In Lume, there's a special file called .tmpl.js that helps you create a dynamic category.
yield {
url: `/category/${category}/`,
title: `Categoryed ${category}`,
type:"category",
category,
};
category.tmpl.js
In lume search.values() have a function that helps you find a category using markdown
meta tags and sends data into the layout/category.njk file. It will generate all categories
with the following URLs like /category/android/ , /category/android-phone/ ,
/category/human/ and so on.
18/28
export const layout = "layouts/tag.njk";
tag.tmpl.js
The following code generates all tags with the following URLs like /tag/android/,
/tag/android-phone/, /tag/human/ and so on.
Lume provides inbuilt search functionality for the site. You enable it with the lume page find
plugin.
19/28
The Lume page finds plugin provides you with a search bar. Simply copy the following code
and paste it into the _config.ts file and restart your server.
pagefind plugin
You configure the plugin in the _config.ts fil. You can also change the default config.
// or
_config.ts
Lume SEO
Lume has a plugin to help with SEO called metas. With the plugin, you can easily add
various SEO-friendly configurations.
You install all plugins within the config.ts file. Copy the following code and paste it into the
config.ts file, then restart the server.
You can configure metas in various ways in the _config.ts file. See the comments below:
20/28
import lume from "lume/mod.ts";
or
To use the SEO metas plugin, you'll need to create a _data.yml file in the root of the project
folder and paste the following code into it:
metas:
site: Minimalist blog
twitter: "@Official_R_deep"
icon: /images/icon.png
lang: en
generator: true
mergedKeys:
metas: object
The following code helps you create all the various SEO tags for your website, and you can
easily extend it with the metas plugin in Lume.
Lume Sitemap
Lume has a plugin called sitemap. This plugin helps you create sitemaps for your blog. With
Lume 13 you do not need to create a sitemap manually.
You install all plugins within the config.ts file. Copy the following code and paste it into the
config.ts file, then restart the server.
21/28
import sitemap from "lume/plugins/sitemap.ts";
You can configure the sitemap plugin in various ways in the _config.ts file. See the
comments below:
import lume from "lume/mod.ts";
import sitemap from "lume/plugins/sitemap.ts";
site.use(sitemap());
// or
You do not need any special file to use the site map plugin. Simply add the plugin after
calling the plugin in config.ts and it'll start working on your site. This creates the
sitemap.xml file and you can change the file name with a custom configuration in
_config.ts file.
You can access the sitemap with the filename, for example by default in the localhost
http://localhost:3000/sitemap.xml and production http://my-domain-
name/sitemap.xml .
Lume Plugins
Lume comes with inbuilt plugins, but you can easily add or remove features according to
your requirements. You do not need all the stuff on your site – you can configure everything
as you wish.
You can add more template engines, minify HTML, CSS, and JavaScript with plugins, enable
code highlighting, date manipulation, image manipulation, SVG support, and more.
22/28
You can also easily create your own plugins with lume. Lume also provides excellent
documentation where you can learn more.
Enable comment
If you want to enable comments on the site, the first step is to install an utterances
application on GitHub. Then, copy and paste the following code into the article read file or
where you show comments on the site.
<script src="https://utteranc.es/client.js"
repo="officialrajdeepsingh/Minimalist-blog"
issue-term="pathname"
theme="github-light"
crossorigin="anonymous"
async>
</script>
Next, you'll need to change the utterance comment script. The first change in the repo
repo="your-github-repo" name is compulsory. The others are not. You can adjust
according to your requirements – for example, changing the theme, issue term, and so on.
To read more about utterance, here's a great article written by Josh Collinsworth.
23/28
The best approach is to add utterance comments in lume and then read the GitHub
discussion.
site.use(netlifyCMS());
To configure it, you'll need to create a /_data/netlify_cms.yml file in the root level and then
paste the following code after restarting your server:
backend:
name: git-gateway
branch: master
media_folder: statics
collections:
- label: Posts
name: posts
description: List of posts
folder: posts
extension: md
create: true
fields:
- label: Title
name: title
widget: string
- label: Content
name: body
widget: markdown
Netlify will ask you for permissions for the CMS proxy. Type npx netlify-cms-proxy-
server in a terminal, press enter or type y, and your Netlify CMS will start running locally
on http://localhost:3000/admin URL. Now your Lume blog is ready for deployment on Netlify.
24/28
How to Deploy Your Blog with Deno Deploy
You can deploy Lume on various platforms such as Deno Deploy, GitHub Pages, Gitlab
Pages, Netlify, Vercel, Fleek, AWS Amplify, and Cloudflare Pages. Lume also provides
excellent documentation on deployment.
In this article, I'm deploying my Lume blog with Deno Deploy (and we'll also see how to do it
with GitHub pages). Deno Deploy is an official platform built by the Deno team to deploy
Deno-based applications.
Before deploying your Lume blog on Deno Deploy, make sure you create a server.ts file in
the root level.
server.start();
console.log("Listening on http://localhost:8000");
Deployment Steps:
25/28
name: Deploy
on: [push]
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
permissions:
id-token: write # Needed for auth with Deno Deploy
contents: read # Needed to clone the repository
steps:
- name: Clone repository
uses: actions/checkout@v3
To deploy Lume on GitHub pages you need to have GitHub Actions set up.
Deployment Steps
1. It's best if you have a GitHub repository so you can convert your local website to
GitHub Pages.
2. Create a new repo and push all your local code into it.
3. Create a new .github/workflows/deno.yml in your project root level, then paste the
following code into it and push it into the GitHub repo. The GitHub action runs based on
the github.yml action and it generates a GitHub page.
26/28
name: Publish on GitHub Pages
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v3
- name: Deploy
uses: crazy-max/ghaction-github-pages@v3
with:
build_dir: _site
env:
GITHUB_TOKEN: ${{ secrets.MY_GITHUB_TOKEN_PAGE }}
github.yml
You need a GitHub token to deploy your Lume website to GitHub pages. This is a required
part of the setup. I found a great article written by Davide that can help you learn more about
GitHub Actions and how to create one.
GitHub Actions takes two or three minutes to finish hosting your website on GitHub Pages.
Check out the GitHub repository to learn how to configure the GitHub workflow for GitHub
pages. You can also see a live demo website on the GitHub page.
A quick note: if you deploy your Lume site on GitHub pages and your image does not show
on the website, there are two possible reasons for this:
1. If all image names aren't in lowercase, you might get an error. To resolve the error,
convert your image names into lowercase with this command: your.github.com/your-
reponame/images/my-image.png
2. If you're using the base_path and relative_urls Lume plugins in your project and
relative_urls is redundant, and then you'll need to remove the relative_urls plugin
in your project. Your image should now work fine.
Conclusion
27/28
Lume is an easy-to-learn and feature-rich static site generator. You can do anything you
imagine with it. Lume gives you a lot of freedom with the code.
The Lume community is not as big as those of Hugo, 11ty, Jekyll, and other tools. But, the
Lume maintainers actively reply to everybody who comments in the GitHub discussion.
Without a strong community, this tool should be able to create a strong impact.
One challenge with Lume is that it's tough to get started for beginners and is more suited to
intermediate and advanced developers. If you're jumping right into using Lume as a
beginner, you might struggle with a lack of background knowledge about how static site
generators work.
Because of this, it's helpful to have a little bit of knowledge about Nuckjunks, JSX, and
other template engines that work based on markdown. Once you gain this experience, then
you'll easily be able to use Lume to design your markdown-based blog.
I recommend using the lume MDX plugin for markdown. You can use JSX-based
components inside the markdown file, and you can create beautiful code blocks, tip blocks,
and so on.
I highly encourage all developers to try Lume out. If you have problems with Lume, you can
reach out to its creator on the GitHub discussion and the Discord server.
If your course is about Computer science, Bioinformatics, and Biotechnology. You can join
my free newsletter.
Rajdeep Singh
JavaScript || React.js || Next.js || Python || Rust || Biotechnology || Bioinformatic ||
Front-end Developer || Author || https://ko-fi.com/officialrajdeepsingh
If you read this far, thank the author to show them you care.
Learn to code for free. freeCodeCamp's open source curriculum has helped more than
40,000 people get jobs as developers. Get started
28/28