Content Files

Non-Markdown files placed in the content/ directory can be automatically published to the output directory. This is useful for images, PDFs, and other assets that live alongside your content.

Configuration

Enable content file publishing in config.toml:

[content.files]
allow_extensions = ["jpg", "jpeg", "png", "gif", "svg", "webp", "pdf"]
disallow_extensions = ["psd", "ai"]
disallow_paths = ["drafts/**", "**/_*"]
Key Type Default Description
allow_extensions array [] File extensions to publish
disallow_extensions array [] File extensions to exclude
disallow_paths array [] Glob patterns for paths to exclude

How It Works

When content file publishing is enabled, Hwaro copies non-Markdown files from content/ to the output directory, preserving their directory structure.

Example

content/
├── about/
│   ├── index.md          → /about/index.html
│   ├── team-photo.jpg    → /about/team-photo.jpg
│   └── resume.pdf        → /about/resume.pdf
├── blog/
│   ├── _index.md         → /blog/index.html
│   └── my-post/
│       ├── index.md      → /blog/my-post/index.html
│       ├── diagram.svg   → /blog/my-post/diagram.svg
│       └── screenshot.png → /blog/my-post/screenshot.png
└── index.md              → /index.html

Files are copied directly to the matching output path. The content/ prefix is stripped automatically.

Extension Matching

Allow List

Only files with extensions in allow_extensions are published:

[content.files]
allow_extensions = ["jpg", "jpeg", "png", "gif", "svg", "webp"]

Extensions are normalized — both "jpg" and ".jpg" are accepted.

Deny List

Exclude specific extensions with disallow_extensions:

[content.files]
allow_extensions = ["jpg", "png", "gif", "svg"]
disallow_extensions = ["psd", "ai", "sketch"]

The deny list takes priority over the allow list.

Path Exclusion

Exclude files by path pattern using glob syntax:

[content.files]
disallow_paths = ["drafts/**", "**/_*", "private/**"]
Pattern Matches
`drafts/**` All files under `content/drafts/`
`**/_*` Any file starting with underscore
`private/**` All files under `content/private/`

Paths are matched relative to the content/ directory.

Referencing Content Files

In Markdown

Reference colocated files with relative paths:

![Team Photo](team-photo.jpg)

[Download Resume](resume.pdf)

![Diagram](diagram.svg)

In Templates

For page bundles, colocated assets are also available via page.assets:

{% for asset in page.assets %}
  {% if asset is matching("[.](jpg|png|gif)$") %}
    <img src="{{ get_url(path=asset) }}" alt="Asset">
  {% endif %}
{% endfor %}

Content Files vs Static Files

Feature Content Files (`content/`) Static Files (`static/`)
Colocated with content ✅ Yes ❌ No
Requires configuration ✅ Yes ❌ No (always copied)
Extension filtering ✅ Yes ❌ No
Path filtering ✅ Yes ❌ No
Best for Per-page assets Site-wide assets

Use content files for assets that belong to specific pages (screenshots, diagrams, attachments). Use static files for site-wide assets (CSS, JS, logos, favicons).

Tips

See Also