Home Documentation Templates Examples Showcase GitHub
Theme

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.

Prefer build errors to broken production links.

A hard-coded relative URL can be syntactically valid HTML while pointing nowhere. @pathto lets Nift catch many of those mistakes while you build.

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.

Checked relationship does not mean transitive build failure.

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 eventEffect on page using @pathto
Concrete target contents modifiedNo rebuild reason; the URL is still valid.
Concrete target deleted/missingReferring page becomes out of date: required path is missing.
Tracked target output missing or producer build failsNo 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-codedProject-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>
A useful rule of thumb.

If the target belongs to this repository/project, consider @pathto. If the target lives elsewhere on the web, use its normal URL.