Content

Content that YiiPress is generating from is stored in the content directory.

Directory structure

content/
├── assets/                        # Global assets (logo, favicon, fonts)
│   └── 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_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

---
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)
  • 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
  • extra — arbitrary key-value pairs accessible in templates

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:

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:

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

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

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:

![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           # language: en (default)
└── hello-world.ru.md        # language: ru

The language suffix in the filename (.ru.md) is a shorthand for setting language: ru in front matter.