hugolify-admin is a Hugo module that generates CMS admin configuration files (YAML/JS/JSON) for multiple headless CMS platforms. It provides a unified abstraction layer over 7 CMS systems so a single Hugo-based site can switch CMS by changing one parameter.
It is not a web app — it produces configuration files consumed by CMS frontends.
- Hugo static site generator (Go templates,
.yml/.html/.jstemplates) - Go module (
go.mod):github.com/hugolify/hugolify-admin - No npm build pipeline — Hugo handles everything
- Output formats:
admin_config_yml(YAML),admin_config_js(JS for Tina),admin_archetypes_json(JSON)
hugolify-admin/
├── content/admin/config.md # Entry point: triggers config generation
├── layouts/
│ ├── admin/
│ │ ├── single.yml # Routes to CMS-specific YAML config partial
│ │ ├── single.js # Routes to CMS-specific JS config partial (TinaCMS)
│ │ └── single.archetypes.json # Generates archetypes JSON
│ └── partials/admin/
│ ├── cms/ # CMS-specific config generators
│ │ ├── decapcms/config.yml # Decap CMS root config
│ │ ├── sveltiacms/config.yml
│ │ ├── cloudcannon/config.yml
│ │ ├── pagescms/config.yml
│ │ ├── staticcms/config.yml
│ │ ├── netlifycms/config.yml
│ │ └── tinacms/
│ ├── collections/ # Collection type definitions
│ ├── blocks/ # Block type definitions
│ │ ├── types/ # One file per block type
│ │ └── fields/ # Block-specific field groups
│ ├── fields/ # Reusable field definitions (200+ files)
│ ├── widgets/ # Widget implementations (one .js per widget)
│ ├── datas/ # Data structure configs (nav, footer, SEO…)
│ ├── func/ # Hugo template utility functions
│ └── archetypes/ # Archetype generation
├── i18n/ # 20 language files (en.yml, fr.yml…)
├── static/admin/ # CSS, logos, email templates
└── hugo.yaml # Module mounts + all default params
content/admin/config.md
→ layouts/admin/single.yml
→ partials/admin/cms/{cms}/config.yml
→ partials/admin/collections/index.yml
→ partials/admin/fields/{field}.yml
→ partials/admin/widgets/{widget}.js
Each field is a .yml partial that calls a widget partial:
# layouts/partials/admin/fields/body.yml
{{- $args := dict
"label" (i18n "admin.fields.body.label")
"name" "body"
-}}
{{ partial "admin/widgets/markdown.js" $args }}
Each widget .js partial handles all CMS variants via if/else if on site.Params.admin.cms:
- CloudCannon → JS object
- Pages CMS → JS object
- Tina CMS → JS object with
namefield - Decap/Netlify/Static/Sveltia CMS → JS object
Example pattern from widgets/string.js:
{{- $cms := site.Params.admin.cms }}
{{ if eq $cms "cloudcannon" }}
...CloudCannon syntax...
{{ else if eq $cms "pagescms" }}
...Pages CMS syntax...
{{ else if eq $cms "tinacms" }}
...Tina CMS syntax...
{{ else }}
...YAML for Decap/Netlify/Static/Sveltia...
{{ end }}All user-facing labels come from i18n/ files. Keys follow the pattern:
admin.fields.{field}.labeladmin.fields.{field}.hintadmin.blocks.{block}.labeladmin.collections.{collection}.label
| CMS | Status | Config file |
|---|---|---|
| Decap CMS | Active | cms/decapcms/config.yml |
| Sveltia CMS | Active | cms/sveltiacms/config.yml |
| Pages CMS | Active | cms/pagescms/config.yml |
| CloudCannon | Beta | cms/cloudcannon/config.yml |
| Tina | Beta | cms/tinacms/ |
| Netlify CMS | Deprecated | cms/netlifycms/config.yml |
| Static CMS | Deprecated | cms/staticcms/config.yml |
The active CMS is set in params:
params:
admin:
cms: decapcms # or sveltiacms, pagescms, cloudcannon, tinacms…Other important params: branch, git, repo, media.*, auth.*, blocks.enable, collections.*, fields.*
- Create
layouts/partials/admin/fields/{field-name}.yml - Build a
$argsdict withlabel(from i18n) andname - Call the appropriate widget partial
- Add i18n keys in
i18n/en.yml(and other languages)
- Create
layouts/partials/admin/widgets/{widget-name}.js - Add a branch for each CMS in the
if/else ifchain - Document accepted params in a comment at the top (see
string.jsfor reference)
- Create
layouts/partials/admin/blocks/types/{block-name}.yml - Optionally add block-specific fields in
blocks/fields/ - Add the block to
hugo.yamlunderparams.admin.blocks.enable - Add i18n keys:
admin.blocks.{block}.label
- Create
layouts/partials/admin/collections/{collection-name}.yml - Register it in the collections index
- Add i18n keys:
admin.collections.{collection}.label
- Create
layouts/partials/admin/cms/{cms-name}/config.yml - Add branches in all widget
.jsfiles - Register the CMS name as a valid value for
params.admin.cms
- This module is imported by Hugo sites — it is not run standalone
- Test changes by using the module in a full Hugo site (
hugo server) - Templates use
.ymlextension but are Go templates processed by Hugo - Widget partials use
.jsextension — they produce text, not actual JS files transform.Remarshal "yaml"is used to clean/normalize YAML output- The
func/partials handle cross-cutting concerns likeGetFields,GetOptions,GetTinaName,GetCloudcannonInputsAndStructures