Using Decap CMS to Add a Web Editor to Your Hugo Site
Hugo is fast and file-based, but its editing experience is entirely command-line: you write Markdown in a text editor, commit to Git, and push to trigger a build. For solo developers this is fine. For publications with non-technical contributors, it is a significant barrier. Decap CMS (formerly Netlify CMS) solves this by adding a browser-based editorial interface to Hugo without abandoning the static site architecture.
What Decap CMS Is
Decap CMS is an open-source, Git-backed content management interface. It sits on top of your Hugo site as a static single-page application, authenticated through your Git provider (GitHub, GitLab, or Bitbucket). When an editor creates or edits content through the Decap interface, Decap commits changes to your Git repository directly. Your build pipeline picks up those commits and rebuilds the site.
The result is a familiar editing UI for contributors while maintaining the static site architecture underneath. Content remains in Markdown files in your repository. Build and deployment work exactly as before.
Adding Decap CMS to a Hugo Site
Step 1: Create the Admin Directory
Create a folder at static/admin/ in your Hugo project. Everything inside static/ is copied verbatim to the built site, so static/admin/ will be accessible at yoursite.com/admin/.
Inside static/admin/, create two files:
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>
<script src="https://unpkg.com/decap-cms@^3.0.0/dist/decap-cms.js"></script>
</body>
</html>
config.yml:
backend:
name: github
repo: your-username/your-repo-name
branch: main
media_folder: static/images/uploads
public_folder: /images/uploads
collections:
- name: "posts"
label: "Posts"
folder: "content/posts"
create: true
slug: "{{slug}}"
fields:
- { label: "Title", name: "title", widget: "string" }
- { label: "Publish Date", name: "date", widget: "datetime" }
- { label: "Draft", name: "draft", widget: "boolean", default: false }
- { label: "Tags", name: "tags", widget: "list", default: [] }
- { label: "Featured Image", name: "featured_image", widget: "string", required: false }
- { label: "Body", name: "body", widget: "markdown" }
Step 2: Configure Authentication
Decap requires OAuth authentication against your Git provider. For GitHub, you need to register a GitHub OAuth app and set up an authentication endpoint.
The simplest path for Netlify-hosted sites is Netlify Identity, which handles the OAuth flow for you:
- Enable Netlify Identity in your site’s Netlify dashboard
- Enable Git Gateway under Identity → Services
- Add this script to your
static/admin/index.html(before the Decap script):
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
And add this to your site’s base template <head> and <body>:
<!-- In <head> -->
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
<!-- Before closing </body> -->
<script>
if (window.netlifyIdentity) {
window.netlifyIdentity.on("init", user => {
if (!user) {
window.netlifyIdentity.on("login", () => {
document.location.href = "/admin/";
});
}
});
}
</script>
For non-Netlify hosting, configure an external OAuth provider or use the implicit flow directly with GitHub.
Step 3: Define Your Content Collections
The config.yml collections array maps to your Hugo content structure. Configure each section of your site:
collections:
- name: "posts"
label: "Posts"
folder: "content/posts"
create: true
slug: "{{year}}-{{month}}-{{day}}-{{slug}}"
fields:
- { label: "Title", name: "title", widget: "string" }
- { label: "Date", name: "date", widget: "datetime" }
- { label: "Draft", name: "draft", widget: "boolean", default: false }
- label: "Tags"
name: "tags"
widget: "list"
field: { label: "Tag", name: "tag", widget: "string" }
- { label: "Body", name: "body", widget: "markdown" }
- name: "pages"
label: "Pages"
files:
- label: "About"
name: "about"
file: "content/about.md"
fields:
- { label: "Title", name: "title", widget: "string" }
- { label: "Body", name: "body", widget: "markdown" }
Available Widgets
Decap CMS provides widgets for common content types:
string— single-line texttext— multi-line text (no Markdown formatting)markdown— rich Markdown editor with previewnumber— numeric inputboolean— true/false toggledatetime— date and time pickerimage— image upload with media libraryfile— file uploadselect— dropdown from defined optionslist— repeating field of any other widget typeobject— grouped fields as a single nested structurerelation— reference to content in another collection
Preview and Workflow
By default, Decap saves content by committing directly to your main branch, which triggers a rebuild. For larger editorial teams, enable the editorial workflow feature:
publish_mode: editorial_workflow
With editorial workflow enabled, new content is saved as a draft PR against the main branch. Editors can move content through Draft → In Review → Ready states in the CMS UI, and content is only merged (and thus published) when explicitly approved.
Limitations to Know
Decap is a good solution for adding a web-based editing interface to Hugo, but it is not a full CMS. Rich document editing features, inline image resizing, complex layout tools, and real-time collaboration are not part of the package. It is best understood as a structured form interface for creating Markdown files — that covers the majority of what independent publishers need, but not everything.
For teams that need a more sophisticated editorial experience, Tina CMS offers a more advanced alternative with inline editing and visual previews directly on the rendered site.