Core concept · Paths
Use paths that know about the project.
@pathto(...) does two useful things at once: it calculates the correct relative path from the output currently being built, and it verifies that the tracked name or concrete local path you referenced actually exists.
A hard-coded relative URL can be syntactically valid HTML while pointing nowhere. @pathto lets Nift catch many of those mistakes while you build.
Tracked page links
When the argument is a tracked name, Nift resolves the target output relative to the page currently being generated.
<nav>
<a href="@pathto('/')">Home</a>
<a href="@pathto('about')">About</a>
<a href="@pathto('docs')">Documentation</a>
<a href="@pathto('docs/commands')">Commands</a>
</nav> The same header can appear at different depths
Imagine one shared header being used to build all of these outputs:
public/index.html
public/about.html
public/docs/index.html
public/docs/guides/install/index.html A literal link such as ../../about.html can only be correct from particular locations. The same @pathto('about') expression can be reused everywhere because Nift knows which output it is currently writing.
Concrete local files
If the argument is not a tracked name, Nift can resolve an existing file path directly. This is extremely useful for project-owned frontend assets.
Stylesheets
<link
rel="stylesheet"
href="@pathto('public/assets/css/site.css')"> JavaScript / compiled TypeScript
<script
type="module"
src="@pathto('public/assets/js/app.js')"></script> It does not matter whether app.js was authored directly or produced by TypeScript, Vite, esbuild, Rollup or another tool. The final local file exists, so Nift can reference it.
Images
<img
src="@pathto('public/assets/images/team.jpg')"
alt="The team">
<img
src="@pathto('public/assets/images/logo.svg')"
alt="Example company"> Responsive images
<picture>
<source
media="(min-width: 900px)"
srcset="@pathto('public/assets/images/hero-large.webp')">
<img
src="@pathto('public/assets/images/hero-small.webp')"
alt="Product interface">
</picture> Favicons and metadata images
<link
rel="icon"
href="@pathto('public/favicon.svg')">
<meta
property="og:image"
content="@pathto('public/assets/images/social-card.png')"> Video and audio
<video
controls
poster="@pathto('public/assets/video/poster.jpg')">
<source
src="@pathto('public/assets/video/demo.mp4')"
type="video/mp4">
</video> Downloads
<a
href="@pathto('public/downloads/product-guide.pdf')"
download>
Download the product guide
</a> @pathto remembers that the target is required
Nift records each resolved @pathto(...) target internally as a requirement in the page's build metadata. Requirements are intentionally not dependencies: changing the target's contents does not change the URL already emitted into the referring page.
@pathto('public/assets/site.css')
│
├─ emit the correct relative URL
├─ verify this concrete path exists while rendering
└─ remember: this concrete path is required to keep existing
@pathto('about')
│
├─ resolve the tracked output path
└─ remember the checked tracked relationship The two forms deliberately differ during later incremental checks. If a concrete project path such as public/assets/site.css disappears, the referring page becomes stale so rebuilding can either repair/remove the reference or fail through the normal missing-target check. If a tracked target such as about has not produced its output yet, or its own build fails, the referring page is still a successful artifact: the tracked producer owns that build result, and the overall site build reports the producer's failure directly instead of creating transitive rebuild/failure noise.
A → @pathto(B) can build A successfully even if B has not built yet or B fails. A full successful site build still cannot finish with B failed, because B is itself a tracked build target. This keeps the rule simple: each tracked page owns its own build success, while @pathto checks and resolves the relationship.
| Target event | Effect on page using @pathto |
|---|---|
| Concrete target contents modified | No rebuild reason; the URL is still valid. |
| Concrete target deleted/missing | Referring page becomes out of date: required path is missing. |
| Tracked target output missing or producer build fails | No transitive rebuild/failure reason for the referrer; the tracked producer owns its own build state. |
There is no @req function and no *.reqs.json file. Requirements are an under-the-hood correctness property of @pathto, not another public dependency system to manage.
What happens when the file is missing?
Suppose the template contains:
<img
src="@pathto('public/assets/images/product-shot.webp')"
alt="Product screenshot"> If that path does not exist, Nift reports the unresolved target during the build. Without the project-aware check, a browser would only discover the problem after somebody requests the generated page.
404 pages
When rendering the tracked page named 404, @pathto(...) emits root-absolute web paths rather than output-relative paths. A deployed 404 page can be served for an arbitrary request depth, so a relative path cannot reliably describe the target from the browser's current URL. Existing target validation and dependency tracking still apply.
<a href="@pathto('docs')">Documentation</a>
<link rel="stylesheet" href="@pathto('public/assets/css/style.css')"> 404 page:
/docs.html
/assets/css/style.css
ordinary page:
docs.html
assets/css/style.css Hard-coded relative paths vs project-aware paths
| Hard-coded | Project-aware |
|---|---|
../../assets/css/site.css | @pathto('public/assets/css/site.css') |
| You count directory depth manually. | Nift calculates it from the current output. |
| A typo may survive the build. | The referenced tracked/local target must resolve. |
| Shared partials become fragile as nesting changes. | The same partial can be reused across output depths. |
Project-local means inside the project
Concrete @pathto(...) targets are deliberately confined to the Nift project. A value such as ../outside.css is rejected even if that file exists. This keeps project-aware path checking and internal reqs self-contained rather than allowing a template to create persistent assumptions about arbitrary files elsewhere on the machine.
When ordinary URLs are still the right answer
@pathto is for targets Nift can know about: tracked outputs and local project files. External URLs remain ordinary URLs:
<a href="https://example.com">External site</a>
<script src="https://cdn.example.com/library.js"></script> If the target belongs to this repository/project, consider @pathto. If the target lives elsewhere on the web, use its normal URL.