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:
| Filename | Convention |
|---|---|
default.md | Accent CMS / Grav convention |
README.md | Repository / documentation convention |
index.md | Web / 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 validatewarns when a page links a file whose extension is neither on the allowlist nor inmedia.attachments.extensions, so a file that would silently vanish from output is visible before you deploy.accent buildpublishes the listed files into the output tree, but static hosting sends noContent-Dispositionheader, so a.shfile 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.