Relative Includes
When a template file moves into a subfolder, PHP relative paths break. Here's the one-time fix.
This is the most common issue encountered when moving templates into subfolders. It is a one-time fix and once done it doesn't matter where a template lives.
The Problem
PHP resolves relative include and require paths from the location of the current file. When a template moves from a flat structure to a subdirectory, those paths break:
// template was at: /site/templates/blog-post.php
// include worked fine:
include("includes/get-widget.inc");
// template moves to: /site/templates/blog/blog-post.php
// PHP now looks for: /site/templates/blog/includes/get-widget.inc
// — that file doesn't exist, so it fails.
This affects include, require, include_once, and require_once — any statement that uses a path relative to the current file.
The Fix
Replace every relative path with an absolute path using ProcessWire's $config->paths->templates, which always points to /site/templates/ regardless of where the calling file lives:
// Before — breaks when the template is in a subdirectory
include("includes/get-widget.inc");
require("includes/functions.php");
// After — works from any depth, forever
include($config->paths->templates . "includes/get-widget.inc");
require($config->paths->templates . "includes/functions.php");
Finding All Occurrences
Run a search across your templates directory to find every instance that needs updating. In most editors or from the terminal:
grep -rn 'include\|require' site/templates/ | grep -v 'config->paths'
This lists every include/require that does not already use $config->paths. Work through the list and update each one.
What About __DIR__?
__DIR__ is another safe option — it resolves to the directory of the file it appears in. However, if you're including a shared file from templates at different depths, $config->paths->templates is cleaner because it gives you an absolute root anchor rather than a relative offset:
// __DIR__ works but requires knowing the depth
include(__DIR__ . "/../includes/get-widget.inc"); // from one level deep
include(__DIR__ . "/../../includes/get-widget.inc"); // from two levels deep — fragile
// $config->paths->templates is always the same regardless of depth
include($config->paths->templates . "includes/get-widget.inc");
Checklist
- Search for
include(andrequire(across all template files - Replace relative paths with
$config->paths->templates . "path/to/file" - Check any shared
_init.phpor_func.phpfiles — these often have their own includes - Check UIkit/chunk files if you use ProcessWire's template engine
- Verify the site loads without errors after moving a template
Once updated, your includes will work correctly from any subfolder depth — both with Stemplates Free (naming convention) and Stemplates PRO (auto-discovery).