How to Add a CMS to Jekyll with Decap CMS (Formerly Netlify CMS)
Add a visual content editor to your Jekyll site using Decap CMS — set up the admin panel, configure collections, and let non-developers publish posts without touching code.
Jekyll is code-first — posts are Markdown files, publishing means a Git commit. That’s great for developers but a barrier for non-technical collaborators. Decap CMS (the open-source successor to Netlify CMS) adds a visual editing interface to any Jekyll site, backed by your Git repository. No database, no separate server.
What Is Decap CMS?
Decap CMS is an open-source, Git-based content management system. It adds an /admin page to your site with a visual editor. When a content editor saves a post, Decap CMS commits the Markdown file to your GitHub repository — the same way you would from the command line, but through a web interface.
Key points:
- Runs entirely in the browser — no server needed
- Saves content as Markdown files in your repository
- Authenticates via GitHub, GitLab, or Bitbucket OAuth
- Free and open-source (MIT licence)
- Works with GitHub Pages, Netlify, and Cloudflare Pages
Step 1: Create the Admin Folder
Create an admin/ folder in your Jekyll site root with two files:
admin/index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="robots" content="noindex">
<title>Content Manager</title>
</head>
<body>
<script src="https://unpkg.com/decap-cms@^3.0.0/dist/decap-cms.js"></script>
</body>
</html>
admin/config.yml:
This is the main configuration file — it defines your content collections and fields.
backend:
name: github
repo: yourusername/your-repo-name
branch: main
media_folder: "assets/images/uploads"
public_folder: "/assets/images/uploads"
collections:
- name: "posts"
label: "Blog Posts"
folder: "_posts"
create: true
slug: "{{year}}-{{month}}-{{day}}-{{slug}}"
fields:
- { label: "Title", name: "title", widget: "string" }
- { label: "Description", name: "description", widget: "string" }
- { label: "Date", name: "date", widget: "datetime" }
- { label: "Cover Image", name: "image", widget: "image", required: false }
- { label: "Author", name: "author", widget: "string", required: false }
- { label: "Category", name: "category", widget: "string", required: false }
- label: "Tags"
name: "tags"
widget: "list"
field: { label: "Tag", name: "tag", widget: "string" }
- { label: "Featured", name: "featured", widget: "boolean", default: false }
- { label: "Body", name: "body", widget: "markdown" }
Step 2: Set Up Authentication
Decap CMS needs a way to authenticate with GitHub. Choose one:
Option A: Netlify Identity (Easiest, Netlify only)
If your site is on Netlify:
- Enable Netlify Identity in your site’s Netlify dashboard → Identity → Enable
- Under Registration preferences, set to Invite only
- Under External providers, enable GitHub
- Add the Netlify Identity widget to your site:
In admin/index.html, add before </head>:
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
Also add to your _layouts/default.html before </body>:
<script>
if (window.netlifyIdentity) {
window.netlifyIdentity.on("init", user => {
if (!user) {
window.netlifyIdentity.on("login", () => {
document.location.href = "/admin/";
});
}
});
}
</script>
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
- Invite users in Netlify → Identity → Invite users
Option B: GitHub OAuth App (Works on any host)
- Go to GitHub → Settings → Developer settings → OAuth Apps → New OAuth App
- Set:
- Application name: Your Site CMS
- Homepage URL:
https://yourdomain.com - Authorization callback URL:
https://api.netlify.com/auth/done
- Copy the Client ID and Client Secret
- In Netlify Dashboard → Site settings → Access control → OAuth → Install provider → GitHub → paste Client ID and Secret
Update admin/config.yml:
backend:
name: github
repo: yourusername/your-repo-name
branch: main
base_url: https://api.netlify.com
auth_endpoint: auth
Step 3: Configure Your Collections
A collection maps to a folder of content files. Configure one for each content type you want editors to manage.
Posts Collection
- name: "posts"
label: "Blog Posts"
folder: "_posts"
create: true
slug: "{{year}}-{{month}}-{{day}}-{{slug}}"
preview_path: "blog/{{slug}}"
fields:
- { label: "Layout", name: "layout", widget: "hidden", default: "post" }
- { label: "Title", name: "title", widget: "string" }
- { label: "Description", name: "description", widget: "string", hint: "150-160 characters for SEO" }
- { label: "Publish Date", name: "date", widget: "datetime" }
- { label: "Cover Image", name: "image", widget: "image", required: false }
- label: "Category"
name: "category"
widget: "select"
options: ["Tutorial", "Blog", "Themes", "SEO", "Comparison"]
- label: "Tags"
name: "tags"
widget: "list"
- { label: "Featured Post", name: "featured", widget: "boolean", default: false }
- { label: "Body", name: "body", widget: "markdown" }
Pages Collection
- name: "pages"
label: "Pages"
files:
- label: "About"
name: "about"
file: "_pages/about.md"
fields:
- { label: "Title", name: "title", widget: "string" }
- { label: "Body", name: "body", widget: "markdown" }
- label: "Contact"
name: "contact"
file: "_pages/contact.md"
fields:
- { label: "Title", name: "title", widget: "string" }
- { label: "Body", name: "body", widget: "markdown" }
Themes Collection (for JekyllHub)
- name: "themes"
label: "Themes"
folder: "_themes"
create: true
slug: "{{slug}}"
fields:
- { label: "Title", name: "title", widget: "string" }
- { label: "Description", name: "description", widget: "text" }
- { label: "Demo URL", name: "demo_url", widget: "string" }
- { label: "GitHub URL", name: "github_url", widget: "string" }
- { label: "Stars", name: "stars", widget: "number" }
- label: "Price Type"
name: "price_type"
widget: "select"
options: ["free", "premium"]
- { label: "Card Image", name: "card_image", widget: "image" }
- { label: "Body", name: "body", widget: "markdown" }
Step 4: Add admin/ to Your Build
Make sure the admin/ folder is included in your Jekyll build. By default it is — Jekyll copies non-underscored folders to _site/.
If you have a .gitignore that excludes admin/, remove that exclusion.
Verify by running bundle exec jekyll build and checking that _site/admin/index.html exists.
Step 5: Exclude admin from Sitemap
You don’t want the admin panel showing up in your sitemap or search results:
# admin/index.html front matter (add front matter to the file)
---
sitemap: false
---
Or in _config.yml:
exclude:
- admin/config.yml
Using the CMS
Once set up, your editors visit https://yourdomain.com/admin/ and log in with GitHub. They see a dashboard with all your configured collections.
Creating a post: Click the collection, click New Post, fill in the fields, write in the Markdown editor, click Publish. Decap CMS commits the file to your repository, triggering a rebuild.
Editing existing content: Browse the collection, click a file, edit, save.
Media uploads: Images drag-and-drop into the Markdown editor and are saved to your assets/images/uploads/ folder.
Editorial Workflow (Optional)
Enable draft/review workflow so posts go through approval before publishing:
# admin/config.yml
publish_mode: editorial_workflow
This adds a Kanban-style board with Drafts, In Review, and Ready columns. Content stays as a branch until approved, then merges to main.
Alternatives to Decap CMS
| Tool | Price | Best for |
|---|---|---|
| Decap CMS | Free | Git-based, self-hosted |
| CloudCannon | $45+/month | Most polished Jekyll CMS |
| Forestry.io | Free (limited) | Simple visual editing |
| TinaCMS | Free (limited) | Real-time inline editing |
| Contentful | Free (limited) | API-first, enterprise |
For most Jekyll sites, Decap CMS hits the right balance of features and cost (free). CloudCannon is worth it if you’re managing content for clients or a team.
Browse Jekyll themes on JekyllHub — all themes work with Decap CMS as the admin panel.