Netlify CMS (Decap CMS)
Now independent as Decap CMS. Git-based CMS for JAMstack. Strong integration with static site generators.
CMS
Netlify CMS (Decap CMS)
Overview
Netlify CMS was rebranded as Decap CMS in February 2023 and is now maintained and developed as an open source project by PM TechHub as a Git-based headless CMS.
Details
Decap CMS (formerly Netlify CMS) is an open source content management system for Git workflows that provides editors with a friendly UI and intuitive workflows. Content is stored in your Git repository alongside your code, enabling version control, multi-channel publishing, and direct content updates in Git.
At its core, it's a React application that acts as a wrapper for Git workflows, using the GitHub, GitLab, or Bitbucket API. Specialized for JAMstack architecture, it's compatible with most static site generators including Hugo, Jekyll, Gatsby, and Next.js. With simple setup, it's widely used for small to medium-sized projects.
Pros and Cons
Pros
- Completely free: Open source with no licensing costs
- Git integration: Unified version control and workflow
- Easy setup: Start with just two files
- Static site specialized: Perfect integration with SSGs
- Platform agnostic: Can be hosted anywhere
- Rich SSG support: Covers major static site generators
- OAuth authentication: Authentication with GitHub/GitLab accounts
Cons
- Basic features: Limited advanced functionality
- No dynamic functionality: Not suitable for real-time content
- UI limitations: Low customizability
- Technical knowledge required: Basic Git and YAML knowledge essential
- Lack of enterprise features: Insufficient for large teams
- Media management constraints: Basic image management
Main Links
- Decap CMS Official Site
- Decap CMS Documentation
- Decap CMS GitHub
- Decap CMS Start Guide
- Decap CMS Community
- SSG Integration Guides
Usage Examples
Basic Setup
<!-- /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>
<!-- Load CMS -->
<script src="https://unpkg.com/decap-cms@^3.0.0/dist/decap-cms.js"></script>
</body>
</html>
Configuration File (config.yml)
# /admin/config.yml
backend:
name: git-gateway # GitHub/GitLab/Bitbucket also available
branch: main # Branch name
# Media folder settings
media_folder: "static/images/uploads"
public_folder: "/images/uploads"
# Collection definitions
collections:
- name: "blog" # Used in URL path
label: "Blog" # UI display name
folder: "content/blog" # File save location
create: true # Allow new creation
slug: "{{year}}-{{month}}-{{day}}-{{slug}}"
fields:
- {label: "Title", name: "title", widget: "string"}
- {label: "Publish Date", name: "date", widget: "datetime"}
- {label: "Featured Image", name: "thumbnail", widget: "image"}
- {label: "Body", name: "body", widget: "markdown"}
Workflow Configuration
# Enable editorial workflow
publish_mode: editorial_workflow
collections:
- name: "pages"
label: "Pages"
folder: "content/pages"
create: true
# Define workflow status
fields:
- {label: "Title", name: "title", widget: "string"}
- {label: "Draft", name: "draft", widget: "boolean", default: true}
- {label: "Content", name: "body", widget: "markdown"}
Next.js Integration
// pages/admin.js
import dynamic from 'next/dynamic'
// Load CMS only on client side
const AdminWithNoSSR = dynamic(
() => import('../components/AdminPanel'),
{ ssr: false }
)
export default function Admin() {
return <AdminWithNoSSR />
}
// components/AdminPanel.js
import { useEffect } from 'react'
export default function AdminPanel() {
useEffect(() => {
;(async () => {
const CMS = (await import('decap-cms-app')).default
CMS.init()
})()
}, [])
return <div id="nc-root" />
}
Custom Widgets
// Custom preview component
CMS.registerPreviewTemplate('blog', BlogPostPreview)
// Register custom widget
CMS.registerWidget(
'color',
ColorControl,
ColorPreview
)
// Custom editor component
CMS.registerEditorComponent({
id: 'youtube',
label: 'YouTube',
fields: [{name: 'url', label: 'YouTube URL', widget: 'string'}],
pattern: /^youtube (\S+)$/,
fromBlock: function(match) {
return {
url: match[1]
}
},
toBlock: function(obj) {
return `youtube ${obj.url}`
},
toPreview: function(obj) {
return `<iframe src="${obj.url}" />`
}
})
Local Development Backend
# Start local backend
npx decap-server
# Configure local backend in config.yml
backend:
name: proxy
proxy_url: http://localhost:8081/api/v1
branch: main # Local branch