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:

YAML
name: Acme
links:
  - /about/

is available in templates as:

PHP
<?= $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:

YAML
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_orderdesc (default) or asc
  • entries_per_page — number of entries per page, 0 for no pagination
  • feedtrue to 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, 0 for unlimited); site-wide feeds use the default limit
  • listingtrue to generate a collection index page (default: true)
  • navigation_pagertrue to 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 — date 2024-01-15, slug hello-world
  • my-post.md — date must be specified in front matter, slug my-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

YAML
---
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 # Heading in 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-DD or YYYY-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
  • drafttrue to 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 as og:image for social sharing. Falls back to the site-level image in config.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_to when 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 false to suppress the generated entry <h1> while retaining the title for metadata and navigation. The legacy showTitle key remains supported; show_title wins 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">, JS window.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; when base_url includes 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_pager is enabled for the collection, set it to false to hide that direction, or provide both text and link for 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 when base_url contains a deployment subdirectory
  • edit_link — set to false to hide the edit-page action for this entry. When omitted, set to null, or set to true, the entry inherits the site-wide edit_page configuration. The legacy editLink key remains supported; edit_link wins when both are present. This does not affect the report-issue action
  • faq_level — controls where [question] shortcodes appear. Omit it or set it to false to keep each question inline, use 0 to collect all questions at the end of the page, or use a heading level from 1 to 6 to 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 toc setting; use false to disable the entry TOC, an integer from 1 to 6 for one heading level, [min, max] for an inclusive range, or deep for levels 2–6. Heading IDs remain available even when the visible TOC is disabled
  • last_updated — set to true or false to override last-updated output for this entry. When omitted or set to null, the entry inherits the site-wide last_updated setting. 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.

YAML
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.

Link to other entries and pages using standard markdown links with relative .md file paths:

MARKDOWN
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.

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:

YAML
title: Pages
permalink: /:slug/
sort_by: weight
entries_per_page: 0
feed: false
listing: false

A page entry:

YAML
---
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:

YAML
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:

YAML
---
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

content/navigation.yaml defines menus:

YAML
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:

YAML
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
<?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 collection
  • content/<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:

MARKDOWN
![Banner](/blog/assets/hello-world-banner.svg)

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.