Using the Netlify CMS locally

I have recently been building a few websites using Eleventy. This is a static site generator, built using Jamstack methodology.

This is a great way to get a simple website built, as it gives you static HTML files which you can serve from anywhere. The downside is there is no database and therefore no traditional content management system to administer the content.

There are tools which help you add a CMS to websites built this way. This is done by using a headless CMS with two options; API-driven and Git-based. I am not using an API to build the content – the content is driven by markdown files, so using a Git-based solution was needed.

Netlify CMS is a drop-in Git-based CMS solution built in React. You create a configuration file and a static HTML file pointing to the JavaScript and you can start editing your content using a nice UI.

Setup with Eleventy

As I have an Eleventy-based project, I used the tools it provided to add the required files. I added a simple /src/admin.md file which generates the /admin/ HTML.

---
title: Content Manager
layout: admin
---

The markdown file uses the /src/layout/admin.njk template. This is very similar to the default Netlify CMS example.

<!doctype html>
<html>
    <head>
        <meta charset="utf-8"/>
        <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
        <title>{{ title }}</title>
    </head>
    <body>
        <script src="{{ "/js/manifest.js" | url }}"></script>
        <script src="{{ "/js/admin.js" | url }}"></script>
    </body>
</html>

In my .eleventy.js configuration file, I added the following. This copies the Netlify CMS configuration YAML file to the /admin/ folder.

config.addPassthroughCopy('src/admin/config.yml', 'admin/config.yml')

Setting up Netlify CMS

I setup the Netlify CMS as described in the Add to Your Site guide. However, if you look at the example template above, I am actually using local JavaScript files.

After getting the basics working, I moved the integration to use locally provided JavaScript. I installed the CMS script using NPM, as per their example. I also required the Netlify Identity widget using the same method.

npm install netlify-cms --save
npm install netlify-identity-widget --save

I have a build step for JavaScript using Laravel Mix, which is based on Webpack. The admin.js file requires the two dependencies imported above.

require('netlify-cms')
require('netlify-identity-widget')

Running locally

The default setup for Netlify is to integrate with an OAuth provider. This is solved using the Netlify Identity which can connect to your GitHub repository.

However, locally, I didn't need this complexity, instead I wanted the CMS to talk to my local repository. Thankfully there is a beta feature called Working with a Local Git Repository which does exactly what I needed.

To enable the local development and authentication, you need update the configuration YAML file and add the following;

local_backend: true

This works by communicating with a local server. You need to install and configure this to make it work. As documented, I used the Netlify CMS Proxy Server

npm install netlify-cms-proxy-server --save-dev

I then updated the package.json scripts, so when starting my Eleventy project, it also started this proxy.

"scripts": {
  "start": "npm run proxy-server & npm run serve & npm run development -- --watch",
  "serve": "cross-env ELEVENTY_ENV=development npx @11ty/eleventy --incremental --serve",
  "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --config=node_modules/laravel-mix/setup/webpack.config.js",
  "proxy-server": "npx netlify-cms-proxy-server",
  "build": "npm run production && npx @11ty/eleventy",
},

Now running npm start gives me a hot-reloaded Eleventy website, with JavaScript and assets built using Laravel Mix and a proxy server to use with the local Netlify CMS.

Development configuration…

The final piece of the puzzle was altering the configuration when running the site locally, in development mode.

I updated the admin.js file, to override configuration based on the current Node build environment. By default, the Netlify CMS is automatically initialised. However, we need to override that. There are some beta features which document exactly what was needed.

import CMS from 'netlify-cms'

require('netlify-identity-widget')

window.CMS_MANUAL_INIT = true

let config = {}

if (process.env.NODE_ENV === 'development') {
  config = {
    local_backend: true
    site_url: 'http://127.0.0.1:8080',
    publish_mode: 'simple'
  }
}

CMS.init({
  config
})

The idea was to set local_backend: true only when needed, in development. However, this only seemed to work when set in the config.yml file, even when other properties such as site_url were successfully overridden correctly.

However, after much investigation, the local_backend: true configuration is only applied on local development. This means that leaving the configuration in the config.yml only affects development.

Although the solution I documented above works, it’s not intuitive to someone looking at the code. There are two issues;

  1. local_backend: true in the JavaScript doesn’t get used.
  2. Setting local_backend: true in the config.yml could frighten/confuse developers who don’t know this only applies locally.