baked.js: integrate content management into a static website generator using JavaScript

Written by Étienne Vallette d'Osia, Developer in Engineering

In a previous article, Rudy explained how to build a website with Content Management using client-side JavaScript and prismic.io. He introduced a JavaScript micro-framework which separates content queries from the presentation templates.

The resulting code with the template would look something like the following:

The process is simple:

  1. Prepare the queries to retrieve content inside prismic-query script tags, and assign them to variables
  2. Use results of your prepared queries in your templates
  3. You get a website with Content Management

One of the advantages of this approach is that since you don't need a dynamic server, you can host your website on any assets server or CDN including DropBox and Github Pages.

In the article, Rudy raised one issue with this approach: since the page generation happens client side, crawlers which don't run JavaScript wouldn't be able to see the website. This turns to be not ideal for Search Engines Optimisation (SEO).

Meet baked.js

This post introduces baked.js, which is the companion of the discussed micro-framework. baked.js is a static website generator, it runs on Node.js to statically execute the provided templates on content retrieved from prismic.io.

The idea is to run baked.js each time changes are published in prismic.io's Writing Room. This frees browsers from executing the templates, and generates an SEO friendly website with content management.

Important note: This is work-in-progress and it will be evolving in the coming days. Also it is an open source project, so please feel free to play with it and don't hesitate if you have ideas for contribution.

Install

To use it, you first need to install its dependencies

git clone https://github.com/prismicio/baked.js.git
cd baked.js
npm install

Use

Run baked.js with the command:

node src/server.js <src_dir> <dest_dir>

It displays a lot of informations, explaining what it's doing, which file it's trying to render, and logs errors that occur.

Tips: you can use the “--no-async” argument to make the displayed informations more readable. It will run slower though.

When it's finished, just copy the generated files into your favorite HTTP server (you can open them directly in your browser if you wish).

Page parameters

Some pages (like articles of a blog) can have dynamically generated URL. This can be done by creating one file and specify parameter, like the ID of the article.

First, add a “<meta>” tag per parameter in the page's header.

<meta name="prismic-routing-param" content="id">

Then use these parameters in your query, by using the syntax “$name” or “${name}.”

<script type="text/prismic-query" data-binding="product">
[
[:d = any(document.id, ["$id"])]
]
</script>

The problem is that these parameters are generally not known before parsing other pages linking to them. For instance the main blog page lists some articles (displaying previews) and gives links to the full article pages.

In baked.js, we use linking pages to infer that there are articles to be generated with these specific IDs. In short, we need to know that a page is needed in order to be able to create it!

To create links to the above page, use the helper “url_to”, and specify the file name (without the “.html” part) and the arguments.

<a href="[%= url_to('product', {id: product.id}) %]">
[%= product.getText('product.name') %]
</a>

Bonus: if your only argument is “id”, you can give it directly, without wraping it in a “{id: "xxx"}” structure.

<a href="[%= url_to('product', product.id) %]">
[%= product.getText('product.name') %]
</a>

You can also use the helper without providing any argument.

<a href="[%= url_to('index') %]">index</a>

Note: remember: if nobody calls a page (using the “url_to” helper) it won't be generated.

Custom URL

It is possible to customize the URL as well. To do so, just add a <meta> tag “prismic-routing-pattern” in your page header.

Be sure to specify every routing parameters in this URL, by using the same syntax as in queries: “$name” or “${name}.”

When you specify parameters, baked.js is capable of detecting URL conflicts and raising the corresponding errors.

<meta name="prismic-routing-pattern" content="product/$id">

Dev mode

When you're developing your website, you still can open source files in your browser without the generation step. This might be preferable for fast prototyping. The rendering micro-framework will run as we discussed in the last blog article (also the “url_to” helper works in dynamic mode too).

Internals

baked.js is built on top of Node.js and use dom.js to emulate the DOM.

It uses Q and lodash, and lets Grunt and Browserify handle the generation of the browser library.

Future improvements

There are plenty of cool things we can do from here:

Better URLs

The present version generates only “.html” files, which is a bit limiting.

We will remove all these “.html” extensions (for instance by generating files like “/foo/index.html” and providing URL like “/foo”). The HTTP server will do the link between the 2.

Dev mode

Because of removing the .html, we won't be able to open files directly into the browser anymore, we'll need an HTTP server during the dev. So we'll add a simple one which binds to the right files.

Such server should support live reloading.

Dynamic mode

Features like releases are not supported yet: the final website is static and it doesn't allow to change the ref. In other words we can't preview other releases with the generated website. Previewing releases is a great feature of prismic.io (actually I'm using it while reviewing this very blog post!), so it's too bad to not have it.

To allow previewing releases, we will combine the static generated website with the dynamic script, this will bring the flexibility of a dynamic website for previewing releases while taking advantage of static generation for a snappy and SEO-friendly website.

Stay in touch

That's it for now, stay tuned, we'll inform you as soon as we have new updates to reveal.