When it comes to personal websites, a static website generator (SSG) is one of the best approaches - in my personal opinion. While amazing products have been developed to serve a more dynamic approach such as Wordpress, NextJS, or even Headless CMS like Contentful, the decision to publish a static website comes to three things: maintability, simplicity and performance.
There's no way to use a static website for a SaaS, but most of personal websites have no use for all the extra flare the dynamic frameworks bring to table.
So I turned my attention to Jigsaw. Having developed with NodeJS and PHP, I still prefer the PHP community (and I don't particularly enjoy the JS "I'll have 10 copies of that" culture).
Jigsaw is very powerful yet extremely simple. Built on top of Blade templating engine, Laravel developers will feel at home while using the product.
I built the website in a few hours. Spent some time on the design, learned Jigsaw and built some theming functions and then improved the website's SEO, accessibility and performance. That last part was a joy to do compared to a React application or a Wordpress theme.
Because it's all HTML in the end, the site just works. With a minimalistic design I keep my responsivity in check and there's not much to work on after I deploy the first version.
But how does Jigsaw works?
First of all, we need to understand PHP was originally very much a templating language. We were - and still are! - able to pass dynamic variables to HTML at ease. But that opens our code to many security holes and makes the whole thing a ticking bomb.
Then comes Blade, a templating engine originally created for Laravel framework, improving and extending some functionalities to our templates.
Now I can separate my header from my main content, my footer from everything else, and add components whenever I want.
Jigsaw also uses Markdown/YAML for its content, so I can write my pages in three different languages if I want. HTML/Blade for the template itself, Markdown or YAML for the content.
Then Jigsaw combines everything together and builds my website transforming it into... HTML again.
I don't have to copy and paste every .html file to create another page and there's no need to keep coding if I want to add another blog post. I can copy the content from my Obsidian files (a markdown content manager and writing tool) directly into my website.
It's like a Headless CMS in a sense that I don't have any interface to help me, but it's so much simpler that I don't need to connect a bunch of services for it to work.
An example from the Jigsaw website:
---
title: "Jigsaw is awesome!"
---
@extends('layouts.master')
@section('content')
<h1>{{ $page->title }}</h1>
<p>Contact us at {{ $page->contact_email }}</p>
@endsection
That means I can have a master layout and extend it with the actual content of the page. I can present dynamic information through the variable $page and directly into my page file I can pass some information as well using YAML front matter such as the title.
It's very pleasant actually.
Another point for Jigsaw that differentiates it from the rest is that it comes with Laravel Mix: a service to connect with Sass and Less processors, Tailwind CSS and webpack so you can compile your assets using NodeJS. Of course, there's no need to use it if you don't want it. But it definitely helped my workflow.
My blog structure
For now, my blog consists in three total pages: home, articles (blogposts) and projects.
Before I build the content for the pages, I've build a template in _layout/main.blade.php where I've put the header and footer. For now everything seems normal, right? Some boilerplate code for this:
<!DOCTYPE html>
<html lang="{{ $page->language ?? 'en' }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@if ($page->canonical)
<link rel="canonical" href="{{ $page->getUrl() }}">
@endif
<meta name="description" content="{{ $page->description ? $page->description . ' | ' : '' }}{{ $page->siteDescription }}">
<title>{{ $page->title ? $page->title . ' | ' : '' }}{{ $page->siteName }}</title>
</head>
<body>
<header>Header content</heeader>
@yield('body')
</body>
<footer>Footer content.</footer>
</html>
The key point in this example is the command @yield('body'). That is some Blade language command to display the content for another page.
And in my general source/ folder I have a index.blade.php with the actual content of the page.
You connect the two by extending the layout. So:
@extends('_layouts.main')
@section('body')
Some content
@endsection
Now for the articles you'll need a page for the actual blog archive and a template for the blogpost itself. This is a copy of my Markdown article:
--------
extends: _layouts.article
section: content
title: My First Blog Post
link: test
date: 2017-03-23
description: This is your first blog post.
tags: ['programming', 'devlog']
--------
This is the start of your first blog post.
The front matter will give Jigsaw enough information to put everything together. The only required information you need to pass is: extends, section, title and the actual content after the lines. Everything else I've put because I wanted more information.
For everything to work now you'll head to your config.php file and add a new collection called posts/articles/anything you want.
This is a copy of my collections section with both the articles and the tags:
'collections' => [
'articles' => [
'author' => 'Daepher', // Default author, if not provided in an articl
'sort' => '-date',
'path' => 'articles/{filename}',
],
'tags' => [
'path' => '/articles/tagged/{filename}',
'articles' => function ($page, $allArticles) {
return $allArticles->filter(function ($article) use ($page) {
return $article->tags ? in_array($page->getFilename(), $article->tags, true) : false;
});
},
],
After playing with it for a few minutes, I realized this is actually a very simple yet powerful feature.
Why Jigsaw is taking SSG to the next level
Tighten, the creators of Jigsaw, are doing an amazing job. There's still more reasons why Jigsaw is actually doing a better job than a lot of other static site generators.
Events
If you look at their starter templates or documentation, you'll see they have three events to hook your application. They are "beforeBuild", "afterCollections" and "afterBuild". I actually use their custom listener to create a sitemap of my blog after build - disclaimer, the one provided in their templates.
Collections
Jigsaw is not only for blogs. They don't have a concept of "posts", instead they have Collections. They work like custom content type for your site. Thinking about it, it's entirely possible to create an online store with collections and a third-party payment app.
With collections you have all the necessary tools to create common usability such as pagination, sorting, indexing and search.
Final thoughts
While lacking some important features (while not core ones) like localization and server-side filtering (this one is possible through indexes! I'll post about it later), Jigsaw seems like a good fit for quick implementation/personal websites.
I hope you enjoyed this overview of the Jigsaw service. This article is also available in my website: https://daepher.com
Top comments (2)
Nice one. Is there any way to generate Table of Contents of a markdown content in Jigsaw?
Yeah! You can create an event hook after build to map through the collection and then create a table of contents with it. I'll code something similar in order to add search functionality with static content.