Content
Content that YiiPress is generating from is stored in the content directory.
Directory structure
content/
├── icon.svg # Site favicon (other image formats are supported)
├── assets/ # Global assets (logo, fonts, downloads)
│ └── logo.svg
├── blog/ # Collection: blog
│ ├── _collection.yaml # Collection config (title, description, pagination settings)
│ ├── assets/ # Blog assets (images for blog entries)
│ │ └── hello-world-banner.svg
│ ├── 2024-01-15-hello-world.md # Entry (date in filename)
│ └── my-second-post.md # Entry (date in front matter)
├── docs/ # Collection: docs (non-blog, sorted by weight)
│ ├── _collection.yaml
│ ├── getting-started.md
│ └── configuration.md
├── page/ # Collection: pages (no listing, no feed)
│ ├── _collection.yaml
│ ├── about.md
│ └── contact.md
├── authors/ # Author definitions
│ ├── assets/ # Author avatars
│ │ └── john-doe.svg
│ ├── john-doe.md
│ └── jane-smith.md
├── data/ # Site data files exposed to templates
│ └── company.yaml
├── config.yaml # Site-wide settings (see docs/configuration.md)
├── navigation.yaml # Menu definitions
└── standalone-page.md # Standalone page (not in any collection)
Site Data
Put YAML files in content/data/ to expose structured site data to templates. Each .yaml or .yml file becomes one key in the $data template variable using the file name without the extension.
For example, content/data/company.yaml:
name: Acme links: - /about/
is available in templates as:
<?= $h($data['company']['name']) ?>
Collections
First-level directories under content/ are collections (e.g., blog/, docs/).
Each collection groups related entries and can have its own pagination, sorting, and template settings.
A collection directory must contain a _collection.yaml file that defines collection-level metadata:
title: Blog description: Latest posts and articles permalink: /blog/:slug/ sort_by: date sort_order: desc entries_per_page: 10 feed: true feed_limit: 20
Collection _collection.yaml fields
- title — collection display name
- description — collection description for meta tags and feeds
- permalink — URL pattern for entries in this collection (see Permalinks)
- sort_by — field to sort entries by:
date(default),weight,title - sort_order —
desc(default) orasc - entries_per_page — number of entries per page,
0for no pagination - feed —
trueto generate RSS, Atom, and JSON Feed files for this collection and include its entries in site-wide/feed.xml,/rss.xml, and/feed.json - feed_limit — maximum entries rendered into each collection feed (default:
20,0for unlimited); site-wide feeds use the default limit - listing —
trueto generate a collection index page (default:true) - navigation_pager —
trueto render previous/next page links from the configured sidebar navigation (default:false)
Entries
Each entry is a markdown file. Front matter is optional — if omitted, the title is inferred from the first # Heading in the markdown body.
File naming
Entries can optionally embed the date in the filename:
2024-01-15-hello-world.md— date2024-01-15, slughello-worldmy-post.md— date must be specified in front matter, slugmy-post
The filename-derived date is used only when date is not set in front matter.
The filename-derived slug is used only when slug is not set in front matter.
Front matter fields
--- title: My First Post date: 2024-01-15 slug: my-first-post draft: false tags: - php - yii categories: - tutorials authors: - john-doe image: /blog/assets/hero.jpg summary: A brief introduction to YiiPress. permalink: /custom/path/ aliases: - /old-path/ - /legacy/path/ layout: post theme: custom weight: 10 language: en redirect_to: /new-url/ extra: custom_field: value ---
- title — entry title; if omitted, inferred from the first
# Headingin the markdown body. Files with no title (neither in front matter nor as H1) are skipped with a console warning - date — publication date (
YYYY-MM-DDorYYYY-MM-DDTHH:MM:SS+00:00); entries with a future date are excluded from build by default (scheduling). Invalid dates fail the build with the source file path. - slug — URL slug; overrides filename-derived slug
- draft —
trueto exclude from build (default:false) - tags — list of tag slugs
- categories — list of category slugs
- authors — list of author slugs (referencing files in
content/authors/) - image — featured image URL (absolute, or root-relative path resolved against
base_url); used asog:imagefor social sharing. Falls back to the site-levelimageinconfig.yaml - summary — manual excerpt; if omitted, auto-generated from content
- permalink — per-entry URL override; takes precedence over collection pattern. Permalinks must be root-relative paths with a trailing slash, must not contain repeated
/,.or..path segments, and must be unique across generated entries and standalone pages. - aliases — old URLs for this entry. YiiPress writes redirect pages from each alias to the entry permalink, or to
redirect_towhen the entry is itself a redirect. Aliases are site-root paths, must be unique across aliases and generated page permalinks, and are not added to feeds, listings, or sitemap - layout — template layout name (default: collection-specific or
entry) - theme — theme name for this entry; overrides the site-level default (see Templates)
- show_title — set to
falseto suppress the generated entry<h1>while retaining the title for metadata and navigation. The legacyshowTitlekey remains supported;show_titlewins when both are present - weight — integer for custom sorting in non-blog collections (lower = first)
- language — language code for multilingual content (e.g.,
en,ru) - redirect_to — URL to redirect to; generates a redirect HTML page (with
<meta http-equiv="refresh">, JSwindow.location.replace(), and<link rel="canonical">) instead of rendering content. Redirect entries are excluded from feeds, sitemaps, listings, archives, and taxonomy pages. Root-relative targets such as/new-url/are YiiPress site-root paths; whenbase_urlincludes a deployment path, YiiPress prefixes that path in the generated redirect target. Absolute URLs are emitted unchanged - previous, next — per-entry pager overrides. Omit a direction to inherit its link from sidebar navigation when
navigation_pageris enabled for the collection, set it tofalseto hide that direction, or provide bothtextandlinkfor a custom link. Custom links also work when collection navigation paging is disabled. Relative and root-relative internal links and absolute HTTP(S) links are accepted; unsafe URL schemes fail the build. Root-relative links are rendered relative to the output page, so they work whenbase_urlcontains a deployment subdirectory - edit_link — set to
falseto hide the edit-page action for this entry. When omitted, set tonull, or set totrue, the entry inherits the site-wideedit_pageconfiguration. The legacyeditLinkkey remains supported;edit_linkwins when both are present. This does not affect the report-issue action - faq_level — controls where
[question]shortcodes appear. Omit it or set it tofalseto keep each question inline, use0to collect all questions at the end of the page, or use a heading level from1to6to collect questions at the end of each matching heading section - toc — controls table-of-contents headings for this entry. Omit it to inherit the site-wide
tocsetting; usefalseto disable the entry TOC, an integer from1to6for one heading level,[min, max]for an inclusive range, ordeepfor levels 2–6. Heading IDs remain available even when the visible TOC is disabled - last_updated — set to
trueorfalseto override last-updated output for this entry. When omitted or set tonull, the entry inherits the site-widelast_updatedsetting. Disabled entries do not read the source modification time - extra — arbitrary key-value pairs accessible in templates
Pager precedence is resolved independently for each direction: an entry override wins, an omitted
override inherits the navigation-derived link, and a missing navigation neighbor produces no link.
previous: false next: text: Advanced guide link: /guide/advanced/
Front matter must be valid YAML key-value pairs. Syntax errors fail the build with the affected file path instead of being ignored.
Internal links
Link to other entries and pages using standard markdown links with relative .md file paths:
Check out [my other post](./2024-03-15-hello-world.md) and the [contact page](../contact.md).
At build time, .md paths are resolved to the actual permalinks. Relative paths (./, ../) are resolved from the current file's directory within the content folder. Non-relative content paths like blog/hello.md also work.
If a .md path does not match any known content file, the link is left unchanged.
Permalinks
Permalink patterns support the following placeholders:
:collection— collection name:slug— entry slug:year— four-digit year from entry date:month— two-digit month from entry date:day— two-digit day from entry date
Default pattern: /:collection/:slug/
Examples:
/:collection/:year/:month/:slug/→/blog/2024/01/hello-world//:year/:slug/→/2024/hello-world/
If an entry has no date, date placeholders remain as-is in the URL.
Pages
There are two ways to create pages:
Standalone pages (root-level)
Markdown files placed directly in the content root directory (not inside any collection) are rendered as standalone pages. The slug is derived from the filename.
content/
├── contact.md # → /contact/
├── privacy-policy.md # → /privacy-policy/
└── ...
Standalone pages support all front matter fields including permalink for custom URLs. They are included in the sitemap and respect --drafts and --future flags.
Collection pages
For more control, use a page collection with its own _collection.yaml:
content/page/_collection.yaml:
title: Pages permalink: /:slug/ sort_by: weight entries_per_page: 0 feed: false listing: false
A page entry:
--- title: About weight: 1 ---
Collection pages support sorting by weight, custom permalink patterns, and all other collection features.
For root-level pages, place _collection.yaml in the content root. This is useful for documentation sites where standalone pages should share collection-level behavior such as navigation_pager without changing their URLs.
Explicit entry order
Instead of relying on sort_by and weight, you can define an explicit entry order in _collection.yaml. This is useful for documentation or guides where you want a specific reading order without introducing artificial sort fields:
title: Docs permalink: /docs/:slug/ listing: true order: - introduction - getting-started - configuration - deployment
When order is set, entries are sorted by their position in the list. The sort_by and sort_order fields are ignored. Entries not listed in order appear after the listed ones.
Authors
Author definitions live in content/authors/. Each file defines one author:
--- title: John Doe email: john@example.com url: https://johndoe.com avatar: /authors/assets/john-doe.svg --- Author bio in markdown.
Author slugs (filenames without .md) are referenced from entry front matter.
Set author_pages: true in content/config.yaml to generate author pages:
/authors/— index page listing all authors with avatars/authors/:slug/— individual author page with bio (rendered from markdown body), contact info, and a list of their entries
When enabled, known authors are linked from entry metadata to their author pages, and author pages are included in the sitemap. With the default author_pages: false, author names remain plain text and no author pages are generated.
Date-based archives
For collections sorted by date (sort_by: date), date-based archive pages are generated automatically:
/:collection/:year/— yearly archive listing all entries for that year, with links to monthly archives/:collection/:year/:month/— monthly archive listing entries for that month, with a back-link to the yearly archive
For example, a blog collection produces /blog/2024/, /blog/2024/03/, /blog/2024/05/, etc.
Taxonomies
Tags and categories are defined inline in entry front matter. Archive pages are generated automatically:
/tags/— all tags/tags/:slug/— entries with a specific tag/categories/— all categories/categories/:slug/— entries with a specific category
Navigation
content/navigation.yaml defines menus:
main: - title: Home url: / - title: Blog url: /blog/ - title: About url: /about/ - title: Docs url: /docs/ children: - title: Getting Started url: /docs/getting-started/ - title: Configuration url: /docs/configuration/ footer: - title: Privacy url: /privacy/ sidebar: - title: Guide children: - title: Getting Started url: /docs/getting-started/ - title: Configuration url: /docs/configuration/
You can define any number of menus. Each item has a title, an optional url, and supports nested children for sub-navigation. Items without url render as section labels, which is useful for grouped sidebars.
In the bundled minimal theme, a menu named sidebar enables a documentation layout for entries whose permalink appears in that sidebar. Those pages render the sidebar navigation on the left and the generated table of contents on the right. Set aside: false in an entry's front matter to hide only its right-hand documentation aside; heading IDs and table-of-contents data remain available to custom templates. Omitting aside (or setting it to true) keeps the default documentation layout. Blog entries and other pages that are not listed in sidebar keep the regular article layout.
To localize menu labels, make title a language map instead of a scalar:
main: - title: en: About ru: О сайте url: /about/ - title: en: Docs ru: Документация url: /docs/ children: - title: en: Getting Started ru: Быстрый старт url: /docs/getting-started/
YiiPress resolves menu labels using the current UI language. If a menu item does not have a label for that language, it falls back to the site's default language, then to English, and finally to the first title value defined for that item. In the bundled minimal theme, changing the remembered UI language also updates rendered menu labels in the header and footer.
Using navigation in templates
Templates receive a $nav variable (a Navigation object). Use NavigationRenderer::render() to output any menu anywhere in a template:
<?php if ($nav !== null && $nav->menu('main') !== []): ?> <?= \YiiPress\Render\NavigationRenderer::render($nav, 'main') ?> <?php endif; ?>
This renders a <nav><ul><li> structure with nested lists for children. You can render as many different menus as needed — just use the menu name from navigation.yaml.
The renderer escapes menu labels and generated attributes with HTML5-compatible Yii helpers, so navigation titles and URLs can contain special characters without breaking markup.
Assets
To add a favicon, put one or more files named icon.svg, icon.ico, icon.png, icon.gif,
icon.webp, icon.avif, icon.jpg, or icon.jpeg directly in content/. YiiPress copies the
files to the output root and the bundled theme adds MIME-aware <link rel="icon"> tags. SVG is
recommended because it stays sharp at every size. Icon URLs work with asset fingerprinting and
sites deployed below a URL subdirectory.
For a custom theme, the detected icons are available as $icons, a list of SiteIcon objects with
path and type properties.
Assets are stored at two levels:
content/assets/— global assets (site logo, favicon, fonts) not tied to any collectioncontent/<collection>/assets/— collection assets (images, files used by entries in that collection)
This keeps assets co-located with the content that uses them.
When a collection is deleted or moved, its assets go with it.
Within a collection assets/ directory, a common convention is to name files after the entry they belong to:
content/blog/assets/
├── hello-world-banner.svg
└── getting-started-screenshot.png
Reference assets from markdown using site-root paths:

All assets/ directories are copied to the build output preserving their path structure.
When asset fingerprinting is enabled, these root-relative asset paths are rewritten relative to each
generated page, which keeps them valid for deployments under a subdirectory such as GitHub Pages
project sites.
The project-level assets/ directory (outside content/) is for build-time assets
(CSS, JS) processed by the asset pipeline. Content assets are separate and copied as-is.
Multilingual content
For multilingual sites, entries can specify a language front matter field.
Translations of the same entry share the same slug but differ in language:
content/blog/
├── hello-world.md # default language
├── cs/
│ └── hello-world.md # language: cs
└── ru/
└── hello-world.md # language: ru
The directory name sets the entry language, so localized files do not need a language
front matter field. An explicit language field takes precedence when present. Standalone
pages use the same convention (content/about.md, content/ru/about.md). This follows
VitePress's locale-directory layout. Locale directory names use lowercase ISO language
codes and may include an uppercase region, for example pt-BR.