This section will help you integrate Netlify CMS with a new or existing Jekyll project.
Jekyll is a blog-aware static site generator built with Ruby. Github Pages are powered by Jekyll, making it a popular choice for developer blogs and project pages.
If you're starting a new project, the fastest route to publishing on a Jekyll website with Netlify CMS is to deploy a template on Netlify.
This guide aims to be helpful for users in the following situations.
- You have an existing Jekyll project and you want to add Netlify CMS
- You have a Jekyll project with Netlify CMS integrated and want to make updates beyond adding content
- You are starting a new Jekyll + Netlify CMS project and you want to start from scratch so you know how everything fits togeter
This guide will use the blog you get if you follow the really excellent official Jekyll step by step tutorial as a starting point. If you're new to Jekyll - I recommended you start by following the tutorial so you know your way around your new blog. Otherwise you can clone this repo and checkout the without-cms
branch.
You have lots of options for hosting and serving your project, but to for the sake of simplicity this guide will assume you follow this guide to create a remote git repository on Github and give Netlify access to build and serve the project when you push changes.
At this point you have a perfectly good Jekyll site published on the web. Congratulations! It looks great, and to update the content on your site you just open your favorite text editor, create or edit a markdown file, then commit your changes and push them to your remote git repository. But maybe you are tired of looking at markdown, or you find yourself wanting to update your blog from computers without your favorite text editor and tools installed, or you want to make it easier for less technical individuals to contribute to your blog. This is where Netlify CMS comes in.
- Create a directory called
admin
in the root directory of your Jekyll project. Jekyll will copy this directory to the_site
generated when thejekyll build
command is run.
mkdir admin
- Move into the admin directory and create the file
index.html
cd admin && touch index.html
- Copy the following HTML into
index.html
.
<!-- admin/index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Content Manager</title>
</head>
<body>
<!-- Include the script that builds the page and powers Netlify CMS -->
<script src="https://unpkg.com/netlify-cms@^2.0.0/dist/netlify-cms.js"></script>
</body>
</html>
- Run
jekyll server
and openhttp://127.0.0.1:4000/admin
in your browser. You should see a page with following error message.
Error loading the CMS configuration
Config Errors:
Error: Failed to load config.yml (404)
Check your config.yml file.
- As indicated in the error message above, Netlify CMS requires a configuration file. When it runs,
netlify-cms.js
will look forconfig.yml
at the root of theadmin
directory. Let's add it now.
touch config.yml
Go back to your browser and you should see the following errors.
Error loading the CMS configuration
Config Errors:
config should have required property 'backend'
config should have required property 'collections'
config should have required property 'media_folder'
config should have required property 'media_library'
config should match some schema in anyOf
Check your config.yml file.
Before we really dig into the configuration for our project, we'll start by setting the minimum configuration to satisfy Netlify CMS.
- For the
backend
property we'll use the test-repo backend. It will let us see the cms interface without connecting to a git repository. - The
media_folder
property should be set to the path where you want the cms to save images. We'll follow the advice of the jekyll docs and create anassets/
directory. Setting themedia_folder
property will take care of themedia_library
property error as well. - The
collections
property requires an array of collection objects. We'll start with a collection with only the propertyname
defined to see what else is required.
Copy and paste the following into config.yml
# config.yml
backend:
name: test-repo
media_folder: "assets/"
collections:
- name: "blog"
Back in the browser you should see a new set of errors.
Error loading the CMS configuration
Config Errors:
'collections[0]' should have required property 'label'
'collections[0]' should have required property 'files'
'collections[0]' should have required property 'folder'
'collections[0]' should have required property 'fields'
'collections[0]' should match exactly one schema in oneOf
Check your config.yml file.
Great, now we're getting somewhere. Netlify CMS is telling us we need to define some properties of collection.
- The
label
property is the string used to identify the collection in the cms UI. We'll set it to"Blog"
folder
should be set to the directory containing the files we want the cms to be able to create and edit. We'll start with"_posts/"
- The
fields
property requires an array of field objects. We'll start with a collection with only the propertyname
defined. Setting thefields
property will take care of thefiles
property error as well.
Update config.yml
like so.
# config.yml
backend:
name: test-repo
media_folder: "assets/uploads"
collections:
- name: "blog"
label: "Blog"
folder: "_posts/"
fields:
- { name: Title }
And in the browser you should see.
Log in and you should see.
Awesome, the CMS is running without errors! Great, but you might have noticed the CMS isn't displaying data from the three markdown files in the _posts/
directory. The reason is test-repo
uses local browser storage and doesn't have access to your file system. In fact, none of the Netlify CMS backends can interact with local git repositories. This is a common point of confusion and bears repeating: NETLIFY CMS CANNOT INTERACT WITH LOCAL GIT REPOSITORIES.
Next, we'll setup the Backend and Authentication so you can start updating content on your Jekyll site.
You have lots of options for giving Netlify CMS permission to push commits to your Jekyll blog repository. In this guide - we're using Netlify's Identity service.
- Replace the
test-repo
backend configurationgit-gateway
//
# config.yml
backend:
name: git-gateway
branch: master # Branch to update (optional; defaults to master)
-
In your browser, go to
https://app.netlify.com/sites/{YOUR_PROJECT_NAME}/settings/identity
-
Click
Enable Identity
-
Scroll down to the Registration section and add external OAuth providers (optional)
-
Scroll down to the Services section and click
Enable Git Gateway
-
Make the following changes to your Jekyll project in
admin/index.html
and_layouts/default.html
. You can read more about the purpose of these changes here
Add the netlify-identity-widget.js
script to the head of admin/index.html
and _layouts/default.html
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
Add the following redirect script to the bottom of the body in _layouts/default.html
<script>
if (window.netlifyIdentity) {
window.netlifyIdentity.on("init", user => {
if (!user) {
window.netlifyIdentity.on("login", () => {
document.location.href = "/admin/";
});
}
});
}
</script>
Now in the browser you should see a modal asking for the URL of your Netlify site.
Once you've set it you should see a Signup/Login modal.
And once you've logged in you should see the CMS UI, now populated with the Jekyll blog posts you created in the Jekyll tutorial. Also note, when using the git-gateway
backend you'll be redirected to your live site when you login, meaning if you want to test your changes you'll need to push to your remote repository.
Click on one of the posts and you'll see the edit view.
Amazing! But the editor is missing some of the frontmatter fields in your markdown file, specifically the layout and author fields.
layout: post
author: jill
title: Bananas
Also, we still can't create new blog posts from the CMS. We'll address these issues and others in the next section.
We'll start by updating the blog
collection. Blogging is baked into into Jekyll, and the _posts/
directory uses some special conventions we'll need to keep in mind as we configure Netlify CMS. Copy and paste the following into your config.yml
.
collections:
- name: "blog"
label: "Blog"
folder: "_posts/"
create: true # Allow users to create new documents in this collection
slug: "{{year}}-{{month}}-{{day}}-{{slug}}" # Filename template, e.g., YYYY-MM-DD-title.md
editor:
preview: false
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: "Body", name: "body", widget: "markdown" }
A few things to note.
- With
create: true
set you'll be able to create new posts - We set the
slug
to'{{year}}-{{month}}-{{day}}-{{slug}}'
because Jekyll requires this format for blog posts.year
,month
, andday
will be extracted from thedate
field, andslug
will be generated from thetitle
field. - We added
editor
configuration with a fieldpreview: false
. This will eliminate the preview pane. Because Jekyll uses Liquid templates, there currently isn't a good way to provide a preview of pages as you update the content. - The
layout
field default is set topost
so Jekyll knows to use_layouts/post.html
when it renders a post. This field is hidden because we want all posts to use the same layout. - The
date
andtitle
field will be used by theslug
- as noted above, Jekyll relies on the filename a post's publish date, but Netlify CMS does not pull date information from the filename and requires a frontmatterdate
field. Note Changing thedate
ortitle
fields in Netlify CMS will not update the filename. This has two implications...- If you change the
date
ortitle
fields in Netlify CMS, Jekyll won't notice - You don't neccassarily need to change the
date
andtitle
fields for existing posts, but if you don't the date in the filenames and frontmatter will disagree in a way that might be confusing - If you want to avoid these issues, use a regular Jekyll collection instead of the special
_posts
directory
- If you change the
In addition to _posts
, the Jekyll tutorial blog includes a collection of authors in the _authors
directory. Before we can configure Netlify CMS to work with the authors
collection, we'll need to make a couple tweeks to our Jekyll blog. Here's the front matter for one of the authors.
short_name: jill
name: Jill Smith
position: Chief Editor
name
has special meaning as a unique identifier in Netlify CMS, but as set up now our Jekyll blog is using short_name
as the unique identifier for authors. For each author, update the frontmatter like so.
name: jill
display_name: Jill Smith
position: Chief Editor
then update _layouts/author.html
and staff.html
accordingly.
<!-- _layouts/author.html -->
--- layout: default ---
<h1>{{ page.display_name }}</h1>
<h2>{{ page.position }}</h2>
{{ content }}
<h2>Posts</h2>
<ul>
{% assign filtered_posts = site.posts | where: 'author', page.name %} {% for
post in filtered_posts %}
<li>
<a href="{{ site.baseurl }}{{ post.url }}">{{ post.title }}</a>
</li>
{% endfor %}
</ul>
<!-- staff.html -->
--- layout: default ---
<h1>Staff</h1>
<ul>
{% for author in site.authors %}
<li>
<h2>
<a href="{{ site.baseurl }}{{ author.url }}">{{ author.display_name }}</a>
</h2>
<h3>{{ author.position }}</h3>
<p>{{ author.content | markdownify }}</p>
</li>
{% endfor %}
</ul>
Next, copy and paste the following into the collections array in config.yml
below the blog
collection.
- name: "authors"
label: "Authors"
folder: "_authors/"
create: true
editor:
preview: false
fields:
- { label: "Layout", name: "layout", widget: "hidden", default: "author" }
- { label: "Short Name", name: "name", widget: "string" }
- { label: "Diplay Name", name: "display_name", widget: "string" }
- { label: "Position", name: "position", widget: "string" }
- { label: "Body", name: "body", widget: "markdown" }
Now that we have the authors
collection configured, we can add an author
field to the blog
collection. We'll use the relation widget to define the relationship between blog posts and authors.
# updated fields in blog collection configuration
fields:
- { label: "Layout", name: "layout", widget: "hidden", default: "post" }
- { label: "Title", name: "title", widget: "string" }
- { label: "Publish Date", name: "date", widget: "datetime" }
- {
label: "Author",
name: "author",
widget: "relation",
collection: "authors",
displayFields: [display_name],
searchFields: [display_name],
valueField: "name",
}
- { label: "Body", name: "body", widget: "markdown" }
With that configuration added, you should be able to select the author for a post from a dropdown.
Our Jekyll blog includes an About page. It would nice to be able to edit that page just like we can edit our blog and author pages. Netlify CMS provides file collections to solve this problem.
Copy and paste the following into the collections array in config.yml
- name: "pages"
label: "Pages"
editor:
preview: false
files:
- label: "About Page"
name: "about"
file: "about.md"
fields:
- { label: "Title", name: "title", widget: "hidden", default: "about" }
- { label: "Layout", name: "title", widget: "hidden", default: "about" }
- { label: "Body", name: "body", widget: "markdown" }
The last aspect of our Jekyll blog we might want to bring under the control of Netlify CMS is our Navigation menu. Our Jekyll tutorial blog has a file _data/navigation.yml
that defines the links rendered by _includes/navigation.yml
. It looks like this.
# _data/navigation.yml
- name: Home
link: /
- name: About
link: /about.html
- name: Blog
link: /blog.html
- name: Staff
link: /staff.html
To make this file editable with Netlify CMS, we'll need to make one minor tweak. The issue is this file contains an yaml array at the top level, but Netlify CMS is designed to work with yaml objects. Update _data/navigation.yml
so it looks like so.
# _data/navigation.yml
items:
- name: Home
link: /
- name: About
link: /about.html
- name: Blog
link: /blog.html
- name: Staff
link: /staff.html
You'll need to update _includes/navigation.html
accordingly. {% for item in site.data.navigation %}
should be changed to {% for item in site.data.navigation.items %}
. When you're done, the nav html should look like this.
<nav>
{% for item in site.data.navigation.items %}
<a href="{{ site.baseurl }}{{ item.link }}" {% if page.url == item.link %}style="color: red;"{% endif %}>
{{ item.name }}
</a>
{% endfor %}
</nav>
Finally, add the following to the collections array in config.yml
- name: "config"
label: "Config"
editor:
preview: false
files:
- label: "Navigation"
name: "navigation"
file: "_data/navigation.yml"
fields:
- {
label: "Navigation Items",
name: "items",
widget: "list",
fields:
- {label: Name, name: name, widget: string}
- {label: Link, name: link, widget: string}
}
Now you can add, rename, and rearrange the navigation items on your blog.