General purpose static pages renderer.

Usage no npm install needed!

<script type="module">
  import staticPagesCore from 'https://cdn.skypack.dev/@static-pages/core';


Static Pages / Core

Build Status Coverage Status npms.io (quality) Maintenance

This package contains only the core; this means it does not provide CLI support or readers and writers. You can import this library to your JS project then add your own controllers, readers and writers.

Yet another static pages generator? Yes! Because I browsed the whole jamstack scene, but could not find one which

  1. uses MVC pattern
  2. can read input from any source (YAML, JSON, front-matter style markdowns, database etc.)
  3. can render with any template engine (Twig, ejs, Pug, Mustache etc.)
  4. supports incremental builds
  5. has a flexible CLI tool (see @static-pages/cli on npm)
  6. has a Docker image (see lionel87/static-pages-js on dockerhub)
  7. written in JS (preferably TypeScript)
  8. easy to extend with JS code
  9. learning and using is easy (Gatsby, Hugo, Jekyll, Eleventy etc. are so cool but harder to learn and configure)

And because I wrote a ton of custom static generators before; I tought I can improve the concepts to a point where its (hopefully) useful for others.

Where should I use this?

This project targets small and medium sized projects. The rendering process tries to be as fast as possible so its also useful when you need performance.


Visit the project page.


Readers provides an iterable list of page data. Controllers can manipulate and extend each data object. Writers render the final output for you.

const staticPages = require("@static-pages/core").default;
const markdownReader = require("@static-pages/markdown-reader").default;
const yamlReader = require("@static-pages/yaml-reader").default;
const twigWriter = require("@static-pages/twig-writer").default;

    from: markdownReader({
        pattern: "pages/**/*.md"
    to: twigWriter({
        view: "content.html.twig",
        viewsDir: "path/to/views/folder",
        outDir: "path/to/output/folder",
    controller: function(data) {
        data.timestamp = new Date().toJSON(); // adds a "timestamp" variable
        return data; // returning the data is required if you want to send it to the renderer
    from: yamlReader({ // assume we have the home page data in yaml format.
        pattern: "home/*.yaml" // <-- reads home/en.yaml, home/de.yaml, home/fr.yaml etc.
    to: twigWriter({
        view: "home.html.twig",
        viewsDir: "path/to/views/folder",
        outDir: "path/to/output/folder",
    controller: function(data) {
        data.commitHash = yourGetCommitHashFn();
        return data;

Options of staticPages()

The staticPages() function expects one parameter which must be an array. Each item should contain from, to and optionally a controller property matching the definition below.

type Options = {
    from: Iterable<Data> | AsyncIterable<Data>;
    to: (data: Data) => void | Promise<void>;
    controller?: (data: Data) => undefined | Data | Data[] | Promise<undefined | Data | Data[]>;
    variables?: Record<string, unknown>;

// Where `Data` is:
type Data = Record<string, unknown>;
// Or the same definition in a more known form:
type Data = { [k: string]: unknown };

Custom options for the controller

You can pass additional configuration options to your controller under the variables property. These variables are accessible through the this context variable of the controller.

    from: ...,
    to: ...,
    controller: function(data) {
        this.myProp; // <-- 123
    variables: {
        myProp: 123,

Missing a feature?

Create an issue describing your needs. If it fits the scope of the project I will implement it or you can implement it your own and submit a pull request.