1 Introduction

Fastpages is a wonderful piece of technology created by FastAI team which enables painless blogging with Jupyter Notebook, Word documents or Markdown. I have been hacking around FastPages to see how far I can strech it to enable blogging with other tools I am interested in learning. I have already blogged about integrating Fastpages with Oberservable notebook, a great place to learn d3 and web visualization; and org mode which is an incredible powerful tool for various scenarios including writing, literate programming or GTD. These workflow are incredibly easy once everything is setup; nevertheless these are all expert level workflows for writing which requires personnel who are little bit adept in technology. Even when you are using a word file, you need to have a dedicated github account, need to upload the file and write commit message.Data Scientists are usually well versed in these, but for people not coming from programming background, these thing can be a bit overwhelming initially. A Content Management System in this regard is excellent tool for focussing on writing and editing rather than programming concepts. In this context, I want to experiment with integration of the same and quickly examine the new workflows possible for content generation.

2 Netlify CMS

Usually CMS are associated with frameworks like wordpress , where content is backed up with a database. For static sites there are no databases. Now in this context, editing workflows are usually driven by structure or organization of your folders/ content . This is sometime also referred to as your content model or data model. Netlify CMS provide workflow to manage the same quite easily from a web interface. Through a clever use of git pull requests, something called Git Gateway to enable login outside git account and react.js , they have come up with a system which can achieve most if not all the functionality of CMS needed by most content editors. So enough chit chat lets get started with some code and steps to implement the integration.

3 Implementation

For doing implementation, I take inspiration from Netlify tutorial to migrate an existing website. Since Fastpages is based on jekyll framework. We will follow the workflow and folder structure recommended for jekyl.

  • First thing we need to do is to create an admin folder in root location ("/") of our project directory.
  • Next add two files to admin folder with following contnet

    • index.html

    Index.html
    Index.html

    • config.html

      backend:
        name: git-gateway
        branch: master # Branch to update (optional; defaults to master)
  • Tutorial above goes into a lot of detail about configuring the config.yml correctly .However I always believe in doing things in small pieces. Making some changes and then looking at the output to figure out what has happened. So now what I do is commit the code. Based on the description presented, we should have an url with admin page in the rendered site. So let's go there and examine the output.
    CMS Error
    CMS Error

The link indicates our configuration is incomplete and gives us recommendation of things that are require fixing. So lets start working on it.

So what do we need? We will add mediafolder and collection. Media library is based on an external service offered by netlify so we won't need it. We will also need to add netlify identity widget in html

  • For media folder add following lines to configuration

    publish_mode: editorial_workflow
    media_folder: images
  • We have two main types of collection in fastpages design : _posts and _pages. We will add both of them as shown below

    collections:
      - name: "post" # Used in routes, e.g., /admin/collections/blog
        label: "Post" # Used in the UI
        folder: "_posts" # The path to the folder where the documents are stored
        create: true # Allow users to create new documents in this collection
        slug: "---" # Filename template, e.g., YYYY-MM-DD-title.md
        fields: # The fields for each document, usually in front matter
          - {label: "Layout", name: "layout", widget: "hidden", default: "post"}
          - {label: "Title", name: "title", widget: "string"}
          - {label: "Publish Date", name: "date", widget: "datetime"}
          - {label: "Categories", name: "categories", widget: "list"}
          - {label: "Body", name: "body", widget: "markdown"}
    
    
    
      - name: "page" # Used in routes, e.g., /admin/collections/pages
        label: "Page" # Used in the UI
        folder: "_pages" # The path to the folder where the documents are stored
        create: true # Allow users to create new documents in this collection
        slug: "" # Filename template, e.g.,title.md
        fields: # The fields for each document, usually in front matter
          - {label: "Layout", name: "layout", widget: "hidden", default: "page"}
          - {label: "Title", name: "title", widget: "string"}
          - {label: "Permalink", name: "permalink", widget: "string"}
          - {label: "Body", name: "body", widget: "markdown"}
  • We will include an identity widget script as below
    Updated Index File
    Updated Index File
  • A custom css script is also added at the bottom of html file after body to make cms mobile friendly.

    @media (max-width: 799px) {
      .css-u4olba-SidebarContainer-card {
        position: static;
        width: 100%;
        margin-bottom: 30px;
      }
      .css-v758ki-AppMainContainer, .css-12b66la-AppHeaderContent {
        min-width: 0;
      }
      .css-104dqk8-AppHeaderButton-button-buttonActive-buttonActive-buttonActive-buttonActive-AppHeaderButton, .css-12yqrwa-AppHeaderNavLink-AppHeaderButton-button-buttonActive-buttonActive-buttonActive-buttonActive-AppHeaderButton {
        margin: 0;
        padding: 16px 7px;
      }
      .css-1f7nhiq-CollectionMain {
        padding-left: 0;
      }
      .css-1hvrgvd-CollectionTopContainer-card-cardTop {
        width: 100%;
      }
      .css-16b5p1f-ToolbarSectionMain-toolbarSection {
        flex-direction: column;
        justify-content: space-evenly;
      }
    
      /* Media */
      .css-1f3mf5k-StyledModal {
        padding: 15px;
        grid-template-rows: 190px auto;
      }
      .css-svjxk-SearchContainer {
        display: block;
        width: 100%;
      }
      .css-smzvtl-LibraryTop {
        flex-direction: column;
      }
      .css-3ifd9s-ActionsContainer {
        text-align: center;
      }
      .css-1ih1y1j-LibraryTitle {
        margin-bottom: 15px;
      }
      .css-2ptjc9-UpperActionsContainer {
        display: inline-block;
      }
      .css-1qukh7n-DownloadButton-button-default-disabled-button {
        margin-left: 0;
        margin-right: 0;
      }
      .css-13rmovq-StyledUploadButton-button-default-disabled-button-gray {
        margin-left: 10px;
      }
      .css-1hpjyse-DeleteButton-button-default-disabled-button-lightRed {
        margin-top: 10px;
        margin-left: 0;
        margin-right: 0;
      }
      .css-1s67tkf-InsertButton-button-default-disabled-button-green {
        margin-top: 10px;
        margin-left: 10px;
      }
      .css-16796rj-LowerActionsContainer {
        display: inline-block;
        margin-top: 0;
      }
      .css-stmjdx-CardGridContainer  {
        position: static;
        overflow: visible;
        width: 100%;
        padding-top: 20px;
      }
      /* End Media */
    
      /* Blog */
      .css-hn3jn7-EditorContainer {
        min-width: 0;
        padding-top: 100px;
      }
      .css-2oej7z-ToolbarContainer {
        height: 100px;
        width: 100%;
        min-width: 0;
      }
      .css-osnbqe-ToolbarToggle {
        flex-direction: column;
      }
      /* End Blog */
    }

    Important: Please add admin.css at bottom not in head, as it needs to override default cms styles

In order to have a working setup; configuration described above is all that we need on netlify cms side. We can push these changes in the repository. Admin page can also be seen in deployed version at <siteurl>/admin. However, it is not operational yet. To make it operational we actually have to configure an identity service.

Now there are multiples ways to do it. One of the more robust way would be to reconfigure actions. However, this would be a very invasive change to overall fastpages structure. My philosphy is to do minimal noninvasive changes so that we can upgrade to newer version easily later. This would mean we need to configure two different site now. One is our original fastpages site and another is our cms site. Our fastpages site may already deployed in gh-pages which are not going to touch. [Although we can easily deploy the same on netlify]. For our admin site we need to do following

  1. Map fastpages repository to netlify. It is a very simple one click install described here
  2. We just need to link the repository and set admin as base directory for our site
    Build Configuration
    Build Configuration
  3. We need to enable identity service and enable git gateway from netlify dashboard. We can also add few user from the dashboard as described in guide below

This will enable the admin site. It can now be used to edit or create post and pages in markdown format without logging into git. You can go to the interface which will look as follows:-

CMS Interface
CMS Interface

You can edit or create a new post on desktop or mobile. A good thing about this setup is you can keep your post in nice looking draft till you have made your all your edits and feel ready to post. You can even add more users ( upto 5 on free netlify account) who can review your article and help you in editing it. They can even post as guest on your blog.

New post
New post

Since everything is working well. We just need to do one final optimization to avoid triggering unnecessary builds when you are drafting a new post. For this we need to ignore ci action on pullrequest event from any branch starting with `cms` as follows:-

name: CI
on:
  push:
    branches:
      - master # need to filter here so we only deploy when there is a push to master
  # no filters on pull requests, so intentionally left blank
  pull_request:
    branches:
      - '!cms**'

4 Conclusion

Fastpages is a fairly opinionated solution with some clever ideas. With it's out of box functionality on notebooks, word documents & markdown; and my extensions on org mode, observable notebook and now netlify cms; I can cover most of my workflows for generating content. Going forward, I will continue to hack around the setup but also post on my actual work related to Data Science, Visualization and Devops. Meanwhile let me know if you find these ideas useful or come up with some kool hacks using this setup. Until then I take your leave.