Vuego automatically loads YAML configuration files from the template filesystem when you create a renderer with NewFS or New(WithFS(...)). This provides global data (site metadata, navigation menus, and theme settings) to every template without requiring explicit Fill() or Assign() calls.
- How It Works
- Directory Structure
- Load Order and Overrides
- Accessing Loaded Data in Templates
- Overriding with Fill
- Examples
- Behavior by Filesystem Type
- See Also
When NewFS() is called, Vuego automatically:
- Loads
theme.ymlfrom the filesystem root (if it exists) - Loads all
.ymland.yamlfiles from thedata/directory (if it exists)
The loaded data is merged into the template's initial context, making it available to all templates as top-level variables.
// Config loading is implicit, no extra options needed
renderer := vuego.NewFS(os.DirFS("templates"))This is equivalent to calling Fill() with the merged YAML data before any Load() or New() call.
templates/
├── theme.yml # Root-level theme config (loaded first)
├── data/ # Data directory (all YAML files loaded)
│ ├── menu.yml # Navigation data
│ ├── site.yml # Site metadata
│ └── footer.yml # Footer configuration
├── layouts/
│ └── base.vuego
├── components/
│ └── Nav.vuego
└── index.vuego
Only two locations are checked:
| Location | What's loaded |
|---|---|
theme.yml |
Single file at the filesystem root |
data/*.yml, data/*.yaml |
All YAML files in the data/ directory |
Files that don't exist are silently skipped. Invalid YAML is also skipped silently.
Files are loaded in this order:
theme.yml(root level)data/directory files (alphabetical order)
Later files override earlier ones at the top-level key. This means data/theme.yml overrides keys from the root theme.yml:
theme.yml (root):
theme:
header:
title: Default Title
subtitle: Default Subtitledata/theme.yml:
theme:
header:
title: Custom Title
subtitle: Custom SubtitleThe resulting context will have theme.header.title set to "Custom Title".
Note: Overrides happen at the top-level YAML key. If both files define a
themekey, the entirethemevalue fromdata/theme.ymlreplaces the one fromtheme.yml.
Loaded data is available as top-level variables in every template:
data/site.yml:
site:
name: My Website
tagline: Built with Vuegodata/menu.yml:
menu:
- label: Home
url: /
- label: About
url: /about
- label: Contact
url: /contactlayouts/base.vuego:
<!DOCTYPE html>
<html>
<head>
<title>{{ site.name }}</title>
</head>
<body>
<nav>
<a v-for="item in menu" :href="item.url">{{ item.label }}</a>
</nav>
<div v-html="content"></div>
</body>
</html>Per-request data passed via Fill() overrides auto-loaded config values. This allows templates to use config as defaults while accepting dynamic data:
renderer := vuego.NewFS(os.DirFS("templates"))
// Fill overrides auto-loaded config values
type PageData struct {
Theme struct {
Header struct {
Title string `json:"title"`
Subtitle string `json:"subtitle"`
} `json:"header"`
} `json:"theme"`
}
data := PageData{}
data.Theme.Header.Title = "Page-Specific Title"
buf := &bytes.Buffer{}
err := renderer.Load("index.vuego").Fill(data).Render(ctx, buf)The precedence order (highest to lowest):
- Front-matter in the
.vuegofile Fill()/Assign()data- Auto-loaded config (
data/files, thentheme.yml)
data/site.yml:
site:
name: Acme Corp
url: https://acme.example.com
description: Building the future
language: enpartials/head.vuego:
<meta charset="UTF-8">
<meta name="description" content="{{ site.description }}">
<title>{{ title | default(site.name) }}</title>
<link rel="canonical" href="{{ site.url }}">data/menu.yml:
menu:
- label: Home
url: /
- label: Products
url: /products
- label: Blog
url: /blog
- label: Contact
url: /contactcomponents/Nav.vuego:
<nav class="main-nav">
<ul>
<li v-for="item in menu">
<a :href="item.url">{{ item.label }}</a>
</li>
</ul>
</nav>theme.yml:
theme:
header:
title: My Site
subtitle: Welcome
colors:
primary: "#3b82f6"
secondary: "#64748b"
footer:
copyright: "© 2025 My Site"layouts/base.vuego:
<!DOCTYPE html>
<html>
<head>
<title>{{ theme.header.title }}</title>
<style>
:root {
--color-primary: {{ theme.colors.primary }};
--color-secondary: {{ theme.colors.secondary }};
}
</style>
</head>
<body>
<header>
<h1>{{ theme.header.title }}</h1>
<p>{{ theme.header.subtitle }}</p>
</header>
<main>
<div v-html="content"></div>
</main>
<footer>{{ theme.footer.copyright }}</footer>
</body>
</html>A complete theme with auto-loaded config:
my-site/
├── theme.yml
├── data/
│ ├── menu.yml
│ └── social.yml
├── layouts/
│ └── base.vuego
├── components/
│ ├── Nav.vuego
│ └── SocialLinks.vuego
└── pages/
└── index.vuego
Go code:
renderer := vuego.NewFS(os.DirFS("my-site"), vuego.WithComponents())
// All config is auto-loaded, just render
err := renderer.Load("pages/index.vuego").Render(ctx, w)No explicit config loading is needed. The renderer automatically picks up theme.yml, data/menu.yml, and data/social.yml.
| Filesystem | Behavior |
|---|---|
os.DirFS(path) |
Config is loaded at construction time. Changes to YAML files on disk are reflected when a new renderer is created. |
embed.FS |
Config is loaded once from the embedded filesystem at construction time. |
vuego.OverlayFS |
Config is loaded from the merged filesystem, with the upper FS taking precedence. |
For development servers that need live reload, create a new renderer instance on each request:
// Development: new renderer per request picks up file changes
func handler(w http.ResponseWriter, r *http.Request) {
renderer := vuego.NewFS(os.DirFS("templates"))
err := renderer.Load("index.vuego").Fill(data).Render(r.Context(), w)
}For production, create the renderer once at startup:
// Production: single renderer, config loaded once
var renderer = vuego.NewFS(embedFS, vuego.WithComponents())- Theme Structuring Guide - Layout organization and directory conventions
- Components Guide - Component composition and props
- Template Syntax - Variable interpolation and directives
- API Reference - Go API documentation