Folder Pages (Grav-Style)

In the folder model, the directory is the page. The folder name becomes the URL, and an index file inside it carries the content. Anything else in the folder – images, PDFs, _-prefixed modules, nested child directories – is scoped to that page. This is the Grav CMS convention, and it is the natural choice when content and its assets travel together.

Layout

content/
  01.home/
    default.md              # page at /home
    hero.jpg                # page-local media for /home
    _hero/
      default.md            # page module
  02.blog/
    default.md              # /blog listing
    01.hello-world/
      default.md            # /blog/hello-world
      cover.jpg             # page-local media
  03.docs/
    default.md              # /docs
    01.getting-started/
      default.md            # /docs/getting-started

Every page is a folder; every folder is reachable at the URL formed from its name. Nesting folders nests URLs, so the filesystem tree and the navigation tree are the same shape.

URLs from folder names

The folder name becomes the last URL segment, with any numeric ordering prefix stripped: 02.blog/ serves /blog, and 02.blog/01.hello-world/ serves /blog/hello-world. The index file’s own name (default.md) never appears in the URL – it only marks which file holds the folder’s content.

Numeric prefixes control order

A leading NN. on a directory name (01., 02., 123.) sets its position in navigation. The prefix is stripped from the URL but recorded as the page’s menu.order, so you get filesystem-driven ordering without writing any frontmatter. Rename 05.guides/ to 02.guides/ and it moves up the menu – no edits to the content itself.

Folders without a prefix sort after prefixed ones, alphabetically by URL.

Index files

The content lives in one of three recognized filenames inside the folder:

FilenameConvention
default.mdAccent CMS / Grav convention
README.mdRepository / documentation convention
index.mdWeb / static-site convention

Filename matching is case-insensitive. If more than one index file is present in the same folder, priority is default.md, then README.md, then index.md – the others are ignored. Most sites pick one convention and use it everywhere; mixing is supported but rarely worth the ambiguity.

Page-local media

Any non-markdown file beside the index is automatically registered as a MediaAsset attached to that page. Drop hero.jpg next to 01.home/default.md and it belongs to /home. In templates, resolve its URL with the media() function, passing the owning page’s URL:

<img src="{{ media('hero.jpg', page.url) }}" alt="Hero">

Because media is scoped to the folder, two different pages can each have their own cover.jpg with no collision – the asset is namespaced by the page it lives beside. See the Media in Templates guide for processing options (resize, crop, format conversion).

Offering file downloads

Only files whose extension is on Accent’s serving allowlist (images, video, audio, PDF, HTML, and common structured-data formats) are served or published; anything else is silently excluded – the safe default, but it also hides a file you want to offer, like an install script or a sample archive. To offer such files as downloads, list their extensions under media.attachments:

media:
  attachments:
    extensions: ["sh", "zip"]

A listed extension becomes resolvable everywhere media is served, but is always delivered as a forced download (application/octet-stream with Content-Disposition: attachment) – the browser saves the file instead of rendering or executing it. Listing an extension that normally renders inline (say pdf) turns those files into downloads too; the setting can only make delivery stricter.

The simplest authoring pattern is the shared media library, which works the same in accent serve and static builds:

[Download the sample init script](/media/downloads/accent-init.sh)

This very docs site serves one this way: download the sample init script. Files placed next to a page’s markdown work too, via the same rules as any page-local media.

Two caveats:

  • accent validate warns when a page links a file whose extension is neither on the allowlist nor in media.attachments.extensions, so a file that would silently vanish from output is visible before you deploy.
  • accent build publishes the listed files into the output tree, but static hosting sends no Content-Disposition header, so a .sh file may display as plain text in the browser instead of downloading. Configure the header at your host if the download behaviour matters there.

Page modules

Subdirectories prefixed with an underscore (_hero/, _features/, _cta/) are composable content blocks, not standalone pages. They never get their own URL; instead they render independently and are exposed to the parent page’s template as page.modules. This is how you assemble a landing page from reusable, separately edited sections:

content/01.home/
  default.md
  _hero/default.md
  _features/default.md
  _cta/default.md

See the Modular Pages guide for how to loop over and render modules in a template.

Template selection

A folder page’s template name is the index filename stem. Since nearly every folder uses default.md, a single default.html.jinja template renders the whole site. Override per page with a template: field in the index frontmatter:

---
title: Home
template: landing
---

Creating a folder page

mkdir -p content/01.about
cat > content/01.about/default.md << 'EOF'
---
title: About Us
menu:
  visible: true
  order: 1
---

Welcome to our about page.
EOF

This produces a page at /about with an explicit title and sort position.

Add media to it

Drop the file in beside the index – nothing else required:

content/01.about/
  default.md        # the page
  team-photo.jpg    # page-local media, accessible via media('team-photo.jpg', page.url)

Add modules to it

Add _-prefixed subdirectories:

content/01.about/
  default.md
  _team/default.md      # rendered into the page as a module
  _values/default.md

When to choose folder pages

  • Your pages carry images, PDFs, or other assets that should stay beside the content and move with it.
  • You want to build landing pages from composable modules (_hero/, _features/, _cta/).
  • You are migrating from Grav CMS.
  • You want filesystem-driven sort ordering with no frontmatter.

When your content is mostly text with few assets, the File Pages model is lighter. The two can also be mixed freely in one site.