This guide helps you understand when and how to regenerate the frontmatter validation schema.
┌─────────────────────────────────────────────┐
│ Did you change frontmatter configuration? │
│ (base theme or module config) │
└─────────────────────────────────────────────┘
│
├─ YES → Regenerate schema (see Step-by-Step below)
│
└─ NO → Continue with normal build
│
├─ Schema exists in data/presidium/? → ✅ Ready to build
│
└─ Schema missing? → Regenerate once, then build
Regenerate the schema whenever you change frontmatter configuration:
Example: Adding an author field to your module config
# config.yml (module)
params:
frontmatter:
author: # ← NEW FIELD
type: string
required: trueAction: ✅ Regenerate schema
Example: Making an existing field required or changing its validation
# Before
status:
type: string
required: false
# After
status:
type: string
required: true # ← CHANGED
options: # ← ADDED
- draft
- publishedAction: ✅ Regenerate schema
Example: Pulling latest changes from presidium-layouts-base
git pull upstream main
# Base theme's config/_default/frontmatter/params.yaml may have changedAction: ✅ Regenerate schema
Example: New module or fresh clone without schema file
ls data/presidium/frontmatter-schema.yaml
# → No such file or directoryAction: ✅ Regenerate schema
Skip schema regeneration in these scenarios:
Example: Writing or editing markdown content
---
title: "My New Article" ← Just adding content
author: "Jane Developer"
---
Content here...Action: ❌ No regeneration needed → Build normally
Example: Schema present and config unchanged
ls data/presidium/frontmatter-schema.yaml
# → File exists
git status config/
# → No changes to configAction: ❌ No regeneration needed → Build normally
Example: Changing site title, menu structure, or other Hugo params
# config.yml
title: "My Updated Site Title" # ← Not frontmatter config
menu:
main:
- name: "About"Action: ❌ No regeneration needed → Build normally
When: After any frontmatter configuration change
# 1. Generate schema to public/
hugo --gc --config config.yml,dependencies.config.yml
# 2. Copy schema to data directory
mkdir -p data/presidium
cp public/frontmatter-schema.yaml data/presidium/frontmatter-schema.yaml
# 3. Clean public directory
rm -rf publicNote: See Technical Deep Dive: dependencies.config.yml for details on the overlay config file.
What happens:
- Hugo reads base theme config:
config/_default/frontmatter/params.yaml - Hugo reads module config:
config.yml→params.frontmatter - Hugo merges both configs
- Schema generator outputs:
public/frontmatter-schema.yaml
When: Every normal build
hugo --gcWhat happens:
- Hugo loads schema from:
data/presidium/frontmatter-schema.yaml - Validates all content frontmatter against schema
- Build fails with errors for invalid frontmatter
- Build succeeds for valid content
Goal: Add a category field that all docs must have
Step 1 - Update module config:
# config.yml
params:
frontmatter:
category:
type: string
required: true
error_message: "Category is required for all documentation"
placeholder_message: "e.g., guides, reference, tutorials"
options:
- guides
- reference
- tutorialsStep 2 - Regenerate schema:
hugo --gc --config config.yml,dependencies.config.yml
mkdir -p data/presidium
cp public/frontmatter-schema.yaml data/presidium/frontmatter-schema.yaml
rm -rf publicStep 3 - Test with build:
hugo --gcExpected: Build errors for content missing category field
Step 4 - Update content:
---
title: "Getting Started"
category: guides # ← Add to all content
---Step 5 - Build again:
hugo --gcExpected: Build succeeds ✅
Goal: Add new article to existing module
Step 1 - Check schema exists:
ls data/presidium/frontmatter-schema.yaml
# ✅ File existsStep 2 - Create content:
---
title: "My New Article"
category: guides
author: "jane@example.com"
---
Content here...Step 3 - Build:
hugo --gcNo schema regeneration needed ✅
Error: Schema file not found at data/presidium/frontmatter-schema.yaml
Solution: Generate schema (Stage 1):
hugo --gc --config config.yml,dependencies.config.yml
mkdir -p data/presidium
cp public/frontmatter-schema.yaml data/presidium/frontmatter-schema.yaml
rm -rf publicError: Frontmatter validation failed for field 'category': required field is missing
Cause: Config changed but content not updated
Solution: Update content to match new schema requirements
Example: Changed required: true but validation still passes without field
Cause: Schema not regenerated
Solution: Regenerate schema (Stage 1) then rebuild (Stage 2)
Example: Module config not overriding base theme config
Check 1 - Config structure:
# ✅ Correct
params:
frontmatter:
title:
required: true
# ❌ Wrong
frontmatter: # Missing "params" wrapper
title:
required: trueCheck 2 - Regenerate schema after fixing structure
Base theme (config/_default/frontmatter/params.yaml):
frontmatter:
title:
type: string
required: true
error_message: "Title required"Module (config.yml → params.frontmatter):
params:
frontmatter:
title:
required: false # ← Override
author: # ← Add new field
type: stringResult (merged):
frontmatter:
title:
type: string
required: false # Module override wins
error_message: "Title required"
author:
type: string # Module addition included| Task | Command |
|---|---|
| Generate schema | hugo --gc --config config.yml,dependencies.config.yml |
| Copy schema | cp public/frontmatter-schema.yaml data/presidium/ |
| Normal build | hugo --gc |
| Check schema exists | ls data/presidium/frontmatter-schema.yaml |
| View schema | cat data/presidium/frontmatter-schema.yaml |
For details on
dependencies.config.yml, see Technical Deep Dive below.
The dependencies.config.yml file is a build overlay that optimizes Stage 1 (schema generation) by excluding all unnecessary Hugo processing. This reduces build time from minutes to seconds when generating only the schema.
Expected location: dependencies.config.yml (repository root)
Hugo supports layered configuration using multiple config files:
hugo --config config.yml,dependencies.config.ymlProcessing order:
- Load
config.yml(includesconfig/_default/directory) - Deep merge
dependencies.config.ymlon top - Later values override earlier values
- Arrays are merged, not replaced
# =============================================================================
# Stage 1: Dependencies Config Overlay
# =============================================================================
# Minimal overlay config for schema generation.
# Used with: hugo --config=config.yml,dependencies.config.yml
#
# This overlay approach allows Hugo to:
# 1. Load config.yml (with config/_default/ including params)
# 2. Overlay these schema-generation-specific settings
#
# USAGE:
# hugo --gc --config config.yml,dependencies.config.yml
# =============================================================================
# Module configuration - exclude content/static/assets, minimal mounts
module:
mounts:
# Exclude all content/static/assets to prevent processing
- excludeFiles: "**"
source: content
target: content
- excludeFiles: "**"
source: static
target: static
- excludeFiles: "**"
source: assets
target: assets
imports:
- path: github.com/spandigital/presidium-styling-base
noMounts: true # Don't need styling for schema generation
- path: github.com/spandigital/presidium-layouts-base
mounts:
- source: layouts
target: layouts
- source: data
target: data
# Disable all page kinds to prevent HTML generation
disableKinds:
- "page"
- "section"
- "taxonomy"
- "term"
- "RSS"
- "sitemap"
- "robotsTXT"
- "404"
# Output ONLY schema (override config.yml outputs)
outputs:
home:
- FrontmatterSchema| Optimization | Impact |
|---|---|
excludeFiles: "**" for content/static/assets |
Skips all content processing |
noMounts: true for styling module |
Excludes unnecessary CSS/SCSS |
disableKinds: [...] |
Prevents all page generation |
outputs: [FrontmatterSchema] |
Only generates schema file |
Result: Build time reduced from ~2 minutes → ~5 seconds
Stage 1 output: public/frontmatter-schema.yaml
This file contains:
- Merged frontmatter configuration (base + module)
- All type defaults applied recursively
- Complete, self-contained schema
Stage 2 requirement: Copy to data/presidium/frontmatter-schema.yaml
Hugo can only load schemas from the data/ directory during builds, but can only generate outputs to public/. This is why the copy step is necessary.
Automated workflow:
# .github/workflows/build.yml example
- name: Generate Schema
run: |
hugo --gc --config config.yml,dependencies.config.yml
mkdir -p data/presidium
cp public/frontmatter-schema.yaml data/presidium/frontmatter-schema.yaml
chmod 444 data/presidium/frontmatter-schema.yaml
rm -rf public
- name: Build Site
run: hugo --gcBenefits:
- ✅ Schema regeneration automated
- ✅ No manual coordination required
- ✅ Consistent schema across builds
- ✅ Reduces developer cognitive load
Problem: dependencies.config.yml not found
Solution: Ensure file exists at repository root:
ls dependencies.config.ymlProblem: Schema not generated
Check: Output configuration
# Verify outputs includes FrontmatterSchema
grep -A 2 "outputs:" dependencies.config.ymlProblem: Build too slow even with dependencies.config.yml
Check: File exclusions working
# Should show excludeFiles for content/static/assets
grep -A 5 "mounts:" dependencies.config.ymlWhen to update:
- Adding new Hugo module imports → Update
module.imports - Changing output formats → Update
outputs - Adding new page kinds → Update
disableKinds
When NOT to change:
- Frontmatter configuration → Edit
config.ymlorparams.yamlinstead - Content structure → This file excludes content
- Styling → This file excludes styling module
- FRONTMATTER.md - Technical reference for validation system
- config/_default/frontmatter/params.yaml - Base theme configuration