Template hierarchy
How Contensio picks which Blade template to render — most-specific-first resolution order for every page type, with examples.
Contensio resolves templates using a most-specific-first strategy: for each request it builds a list of candidate template names in order of specificity, then renders the first one that exists in the active theme. If none exist, it falls back to index.blade.php.
This mirrors how WordPress template resolution works — if you've ever created a single-product.php to override WooCommerce, the concept is identical.
The resolution is handled by Contensio\Support\ThemeTemplateResolver. It checks candidates via view()->exists("theme::{$template}"), so no route changes or controller modifications are needed — you just create the file.
Resolution tables
Homepage
Triggered by the homepage route. The resolver checks whether the reading setting is set to Latest posts or A static page and builds the candidate list accordingly.
| Priority | Template | When used |
|---|---|---|
| 1 | front-page.blade.php |
Always checked first — custom landing page |
| 2 | page.blade.php |
Static page mode only |
| 2 | home.blade.php |
Latest posts mode only |
| 3 | index.blade.php |
Ultimate fallback |
// Internal resolution (simplified)
ThemeTemplateResolver::home(isStaticPage: true);
// checks: front-page → page → index
ThemeTemplateResolver::home(isStaticPage: false);
// checks: front-page → home → index
Single content item (post, page, custom type)
| Priority | Template | Example |
|---|---|---|
| 1 | single-{type}-{slug}.blade.php |
single-post-hello-world.blade.php |
| 2 | single-{type}.blade.php |
single-post.blade.php, single-product.blade.php |
| 3 | single.blade.php |
Generic single item |
| 4 | index.blade.php |
Fallback |
Blog post — slug "hello-world":
single-post-hello-world.blade.php ← most specific
single-post.blade.php
single.blade.php
index.blade.php
Custom type "recipe" — slug "carbonara":
single-recipe-carbonara.blade.php
single-recipe.blade.php
single.blade.php
index.blade.php
Note on
post.blade.php: The old convention usedpost.blade.phpfor blog posts. That still works in the default theme as a kept file, but new themes should usesingle-post.blade.php— it follows the same naming pattern as all other content types.
Standalone page
| Priority | Template | Example |
|---|---|---|
| 1 | page-{slug}.blade.php |
page-about.blade.php |
| 2 | page.blade.php |
|
| 3 | index.blade.php |
page-about.blade.php ← for the /about page specifically
page.blade.php
index.blade.php
Use page-{slug}.blade.php when a specific page needs a completely different layout (e.g., a full-screen landing contact page).
Content type archive
| Priority | Template | Example |
|---|---|---|
| 1 | archive-{type}.blade.php |
archive-recipe.blade.php |
| 2 | archive.blade.php |
|
| 3 | index.blade.php |
Taxonomy term archive
| Priority | Template | Example |
|---|---|---|
| 1 | taxonomy-{slug}.blade.php |
taxonomy-genre.blade.php |
| 2 | category.blade.php |
Hierarchical taxonomies |
| 2 | tag.blade.php |
Flat (non-hierarchical) taxonomies |
| 3 | taxonomy.blade.php |
|
| 4 | archive.blade.php |
|
| 5 | index.blade.php |
Contensio passes is_hierarchical from the Taxonomy model to the resolver, so category.blade.php is used for tree-structured taxonomies (departments, categories) and tag.blade.php for flat ones (tags, labels).
taxonomy-genre.blade.php ← if taxonomy slug is "genre"
category.blade.php ← hierarchical (is_hierarchical = true)
tag.blade.php ← flat (is_hierarchical = false)
taxonomy.blade.php
archive.blade.php
index.blade.php
Author archive
| Priority | Template |
|---|---|
| 1 | author.blade.php |
| 2 | archive.blade.php |
| 3 | index.blade.php |
Search results
| Priority | Template |
|---|---|
| 1 | search.blade.php |
| 2 | index.blade.php |
404 Not found
| Priority | Template |
|---|---|
| 1 | 404.blade.php |
| 2 | index.blade.php |
Practical examples
Override just one post
You have a post with the slug annual-report-2025 that needs a completely different layout — full-width, no sidebar, print-friendly:
views/
single-post-annual-report-2025.blade.php ← only this post
single-post.blade.php ← all other posts unchanged
No route changes, no controller modifications. Just create the file.
Add a custom content type archive
You've registered a recipe content type and want a grid layout instead of a list:
views/
archive.blade.php ← existing — list layout
archive-recipe.blade.php ← new — grid layout for recipes
Taxonomy-specific layout
You have a genre taxonomy for music reviews and want it to look different from your post tags:
views/
tag.blade.php ← generic flat taxonomy layout
taxonomy-genre.blade.php ← genre-specific layout with a banner
Minimum viable theme
A minimal theme only needs layout.blade.php and index.blade.php. Everything else is optional — the resolver falls back gracefully:
views/
layout.blade.php required
index.blade.php required — renders $posts if available, otherwise site name
A theme that adds progressively more specificity:
views/
layout.blade.php
index.blade.php ← fallback
home.blade.php ← blog homepage
single-post.blade.php ← blog posts
page.blade.php ← static pages
taxonomy.blade.php ← any term archive
Differences from WordPress
| Aspect | WordPress | Contensio |
|---|---|---|
| Resolution mechanism | File scan at request time | view()->exists() check via ThemeTemplateResolver |
| Post template file | single-post.php |
single-post.blade.php |
| Page-by-slug | page-{slug}.php |
page-{slug}.blade.php |
| Custom type single | single-{type}.php |
single-{type}.blade.php |
| Custom type archive | archive-{type}.php |
archive-{type}.blade.php |
| Taxonomy specific | taxonomy-{slug}.php |
taxonomy-{slug}.blade.php |
| Hierarchical taxonomy | category.php |
category.blade.php |
| Flat taxonomy | tag.php |
tag.blade.php |
| Static homepage | front-page.php |
front-page.blade.php |
| Blog homepage | home.php |
home.blade.php |
| Ultimate fallback | index.php |
index.blade.php |
The hierarchy is intentionally similar to WordPress so migrating theme logic is straightforward. The main differences are .blade.php extension, the theme:: namespace, and Blade syntax (@extends, @section, @yield) instead of WP template parts.
See also
- Creating a theme — full guide with variable reference and hook integration
- Contensio themes vs WordPress themes — side-by-side comparison