Hugo's Cascade Configuration: How Settings Propagate Through Your Site

Hugo's cascade configuration allows you to define front-matter values in site configuration or ancestor pages that automatically propagate to all descendant pages unless overridden by a closer ancestor or the page itself.

Hugo's cascade configuration system, implemented in the gohugoio/hugo repository, solves the problem of repetitive front-matter by enabling inheritance of parameters across the content tree. This feature allows developers to set default values for entire sections, languages, or page kinds without duplicating configuration on every individual page.

Understanding Hugo's Cascade Configuration

The cascade system relies on three core components working together to propagate settings:

  • Cascade map or slice – Holds the values (params, build, custom fields) that should be inherited, parsed by page.DecodeCascadeConfig in resources/page/page_matcher.go【/cache/repos/github.com/gohugoio/hugo/master/resources/page/page_matcher.go#L151-L182】.
  • Target matcher – Limits cascade application to specific pages by kind, path, language, or environment, implemented via page.PageMatcher【/cache/repos/github.com/gohugoio/hugo/master/resources/page/page_matcher.go#L27-L48】.
  • Early compilation – Applies cascade values before regular front-matter processing in PageConfigEarly.CompileEarly【/cache/repos/github.com/gohugoio/hugo/master/resources/page/pagemeta/page_frontmatter.go#L302-L363】.

Declaring Cascade Settings: Site Config vs. Front Matter

You can define cascades in two locations, each with different scope:

Site configuration (config/_default/config.toml or config.yaml) applies globally to the entire site:

[cascade]
  [cascade.params]
    color = "blue"
    author = "Admin"

[[cascade]]
  [cascade.params]
    draft = true
  [cascade.target]
    kind = "page"
    path = "posts/**"

Page front matter (typically in _index.md files) applies to that branch of the content tree:


# content/blog/_index.md

cascade:
  params:
    color: "green"
  target:
    kind: "page"

The front-matter documentation defines the cascade field and points to the full description in the Hugo source【/cache/repos/github.com/gohugoio/hugo/master/docs/content/en/content-management/front-matter.md#L49-L51】.

Parsing the Cascade Configuration in page_matcher.go

When Hugo reads a page, it looks for the cascade key and processes it through page.DecodeCascadeConfig:

func (m *pageMetaSource) setCascadeFromMap(frontmatter map[string]any, ...) error {
    const pageMetaKeyCascade = "cascade"
    if cv, found := frontmatter[pageMetaKeyCascade]; found {
        cascade, err := page.DecodeCascadeConfig(cv)
        m.cascadeCompiled = cascade
    }
}

The DecodeCascadeConfig function in resources/page/page_matcher.go converts the raw map into a CascadeConfig struct containing a slice of PageMatcherParamsConfig objects. It validates allowed keys such as params and target【/cache/repos/github.com/gohugoio/hugo/master/resources/page/page_matcher.go#L151-L182】.

The target matcher is built by cascadeConfigDecoder.decodePageMatcher, which supports matching on:

  • kind – page kind (page, section, home, taxonomy, term)
  • path – glob pattern against the page's absolute path
  • lang – language code for multilingual sites
  • environment – build environment (production, development)

All values are normalized (lower-cased, slashes fixed) during decoding【/cache/repos/github.com/gohugoio/hugo/master/resources/page/page_matcher.go#L33-L48】.

How Cascade Propagates Through Early Compilation

Once parsed, cascades are applied early in the page build process via PageConfigEarly.CompileEarly in resources/page/pagemeta/page_frontmatter.go. This happens in two distinct passes:

First pass – Apply cascades without site filtering:

for cascade := range cascades.All() {
    if cascade.Target.SitesMatrixCompiled != nil { continue }
    if !cascade.Target.Match(p.Kind, pi.Base(), conf.Environment(), p.SitesMatrix) { continue }
    for ck, cv := range cascade.Fields {
        if done := p.setCascadeEarlyValueIfNotSet(ck, cv); done { break }
    }
}

Second pass – Apply cascades with site filtering (multilingual support):

for cascade := range cascades.All() {
    if cascade.Target.SitesMatrixCompiled == nil { continue }
    if !cascade.Target.Match(p.Kind, pi.Base(), conf.Environment(), p.SitesMatrix) { continue }
    // Apply cascade fields...
}

If a site-specific cascade changes the matrix, Hugo rebuilds the matrix via buildSitesMatrixFromSitesConfig to ensure subsequent cascades see the correct context【/cache/repos/github.com/gohugoio/hugo/master/resources/page/pagemeta/page_frontmatter.go#L302-L363】.

The actual value injection uses setCascadeEarlyValueIfNotSet, which currently handles special cases like the sites field (updating the page's site matrix) while deferring other parameters to the final Params map merge【/cache/repos/github.com/gohugoio/hugo/master/resources/page/pagemeta/page_frontmatter.go#L22-L28】.

Accessing Inherited Values in Templates

After early compilation completes, cascade values are available in templates through the standard .Params map. The inheritance follows this priority:

  1. Page front-matter values (highest priority, override everything)
  2. Cascade values from the nearest matching ancestor
  3. Global cascade values from site configuration (lowest priority)

Access inherited parameters in templates like this:

{{/* Retrieve inherited color with fallback */}}
{{ $color := .Params.color | default "black" }}
<div style="color: {{ $color }}">{{ .Title }}</div>

If a page defines color = "red" in its front matter, that value wins; otherwise, Hugo serves the value from the nearest cascade entry that defined color.

Multilingual Cascade Example

For multilingual sites, cascades can target specific languages using the sites.matrix.languages target. Consider this site configuration:


# config.yaml

cascade:
  - params:
      author: "Admin"
    target:
      sites:
        matrix:
          languages: ["en"]
  - params:
      author: "Admin-NO"
    target:
      sites:
        matrix:
          languages: ["nn"]

Combined with section-level cascades in front matter:


# content/blog/_index.md

[[cascade]]
  [cascade.params]
    theme = "dark"
  [cascade.target]
    kind = "page"
    path = "blog/**"

English pages inherit author: Admin while Nynorsk pages receive author: Admin-NO. Both language versions inherit theme: dark for pages under blog/ because that cascade lacks a site filter.

Key Source Files and Implementation Details

The cascade system spans several critical files in the Hugo codebase:

  • resources/page/page_matcher.go – Contains DecodeCascadeConfig and PageMatcher logic for parsing cascade configurations and target matching【/cache/repos/github.com/gohugoio/hugo/master/resources/page/page_matcher.go#L151-L182】【/cache/repos/github.com/gohugoio/hugo/master/resources/page/page_matcher.go#L27-L48】.

  • resources/page/pagemeta/page_frontmatter.go – Implements PageConfigEarly.CompileEarly for the two-pass cascade application and site matrix handling【/cache/repos/github.com/gohugoio/hugo/master/resources/page/pagemeta/page_frontmatter.go#L302-L363】【/cache/repos/github.com/gohugoio/hugo/master/resources/page/pagemeta/page_frontmatter.go#L22-L28】.

  • hugolib/page__meta.go – Handles reading cascade from front matter via setCascadeFromMap and stores the compiled cascade in pageMetaSource.

  • docs/content/en/content-management/front-matter.md – Documents the cascade front-matter field【/cache/repos/github.com/gohugoio/hugo/master/docs/content/en/content-management/front-matter.md#L49-L51】.

  • docs/content/en/configuration/cascade.md – Comprehensive guide for site-wide cascade configuration.

Summary

Hugo's cascade configuration provides a powerful inheritance mechanism for front-matter values across your site's content tree. Key takeaways include:

  • Define once, inherit everywhere – Set parameters in config.toml or ancestor _index.md files to avoid repeating front-matter on every page.
  • Target matching – Use cascade.target with kind, path, lang, or environment to scope cascades to specific page subsets.
  • Two-pass compilation – Hugo applies cascades early in PageConfigEarly.CompileEarly, first for global cascades then for site-specific (multilingual) ones.
  • Override capability – Descendant pages can override inherited values by defining the same keys in their own front-matter.

Frequently Asked Questions

How do I override a cascade value on a specific page?

To override an inherited cascade value, simply define the same parameter key in the specific page's front-matter. Hugo's PageConfigEarly.CompileEarly processes page-level values after applying cascades, ensuring that local front-matter takes precedence over inherited values from resources/page/pagemeta/page_frontmatter.go.

Can I apply cascades to specific languages only?

Yes, use the target.sites.matrix.languages key in your cascade configuration to restrict application to specific language codes. During the second pass of CompileEarly, Hugo checks cascade.Target.SitesMatrixCompiled to match against the page's language matrix, allowing you to set language-specific defaults while sharing other cascades across all languages.

What is the difference between cascade in config.toml versus front matter?

Cascades defined in config.toml (or config.yaml) apply globally to the entire site or filtered subsets based on target matchers. Cascades defined in page front matter (typically in _index.md files) apply only to that branch of the content tree and its descendants. The page.DecodeCascadeConfig function in resources/page/page_matcher.go processes both sources identically, but their scope differs based on where they are declared.

How does Hugo handle cascade conflicts when multiple ancestors define the same parameter?

Hugo resolves cascade conflicts through proximity-based inheritance. When CompileEarly processes cascades in resources/page/pagemeta/page_frontmatter.go, it applies values from the nearest matching ancestor first. If multiple cascades match, closer ancestors in the content tree override more distant ones, while the page's own front-matter overrides all inherited values.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →