Home Documentation Templates Examples Showcase GitHub
Theme

Core concept

Incremental builds.

During development, rebuilding everything after every edit is wasteful. Nift keeps track of what each generated file depends on so it can rebuild the outputs affected by your change instead of treating the whole project as dirty.

Preview before you build.

nift status runs the same dependency analysis without changing outputs. It lists affected pages and reasons such as a changed dependency, missing generated output or missing build metadata. Use status -p when you want every page/reason instead of a compact fan-out summary.

The everyday loop

# edit content/templates/assets...
nift build

# edit again...
nift build

With no tracked names supplied, nift build dispatches to the updated-build path. That makes the normal development command intentionally boring: edit something, build what changed, refresh.

A shared partial can affect many pages

templates/partials/header.html
            │
            ├── index
            ├── about
            ├── docs/getting-started
            └── docs/configuration

If those pages all include the header, Nift has enough dependency information to know they are affected when the header changes. A completely unrelated page does not need to be rebuilt just because it exists in the same project.

A content edit can stay local

content/about.html changed
        ↓
public/about.html rebuilt

content/contact.html unchanged
        ↓
public/contact.html left alone

That difference becomes increasingly valuable as a site grows from a handful of pages into hundreds, thousands or more.

Choosing modified, hash or hybrid

The three incremental modes differ in what they trust. The choice is not simply “old fast mode versus new correct mode”; it should match the filesystem and deployment workflow in which builds actually run.

ModeChange signalBest fit
modifiedA dependency's modification time is newer than the stored page-build metadata.Persistent local working trees where mtimes are cheap and meaningful.
hashThe dependency's current content hash differs from Nift's stored successful-build hash.Fresh checkouts, CI/deployment runners, containers, restored workspaces and copied trees.
hybridRebuild when either the mtime check or content-hash check reports a change.Workflows that want both signals and accept the cost of hashing dependencies.

Why modified is excellent locally

In a long-lived development checkout, editing a file normally advances its modification time. Reading that metadata is extremely cheap: Nift can reject unchanged dependencies without reading and hashing their full contents. For the ordinary edit → build → refresh loop, modified is often the fastest and simplest signal.

Why fresh checkouts change the trust model

A remote builder does not inherit the meaningful timestamp history of the working tree where a commit was authored. Git, a deployment service, a container build or a restored workspace may materialize files with new mtimes that describe the checkout operation rather than the content's relationship to Nift's previous successful build.

previous successful build: dependency contents = A
        ↓
commit / push / fresh remote checkout
        ↓
filesystem timestamps are recreated
        ↓
mtime describes materialization, not content identity

Hashes survive that boundary conceptually: the same bytes produce the same stored identity, while changed bytes produce a different one. That makes hash particularly appropriate for Git-push deployment workflows, Vercel or Netlify builds, CI runners, containers, cloned repositories and restored caches—provided the corresponding .nift hash state is available from the previous successful build or cache.

stored hash = X
checkout hash = X  → content unchanged
checkout hash = Y  → content changed
A practical default rule.

Use modified when builds run against a persistent working tree whose modification times are meaningful. Use hash when builds may run on fresh checkouts or restored/distributed environments where timestamps do not reliably represent content history.

What hybrid means today

Current hybrid mode rebuilds when either signal reports a change: a dependency is newer than the stored page metadata or its content hash differs from the stored hash. It is intentionally the broadest detector, not a metadata prefilter that always avoids hashing. Choose it when that belt-and-braces behavior is worth the extra file reading; choose hash when content identity is the decisive portable signal.

Hash state is build state

Hash mode is strongest when the stored hashes from the last successful build cross the environment boundary too. If they are absent, Nift conservatively treats dependencies as changed and establishes fresh state during a successful build. In ephemeral CI, cache or restore the relevant .nift state when you want unchanged builds to retain their incremental advantage.

Why it feels good during development

Fast feedback

The cost of a tiny edit can stay close to the cost of rebuilding the pages that actually use it.

Lower resource use

Skipping unnecessary work means less CPU time, less disk churn and less energy spent repeatedly regenerating identical files.

Quieter machines

On larger projects, avoiding repeated full rebuilds can also mean fewer needless CPU spikes and, yes, less time listening to your laptop/NUC fans announce that you changed one paragraph. XD

Scales with the dependency graph

A project does not need to rebuild as one indivisible blob. Shared changes can fan out where needed while isolated changes stay isolated.

Incremental vs full build

# Normal development (incremental)
nift build

# Deliberately rebuild every tracked output
nift build --all

build --all remains useful when you explicitly want a clean full pass—for example before measuring full-build performance or when you simply want every output regenerated. But it should not have to be your default response to every edit.

Continuous updated builds

nift build --auto

build --auto watches for changes and rebuilds updated outputs while you work, polling on a fixed 200 ms interval. Press q in an interactive terminal to stop.

Incremental builds are part of the workflow, not a benchmark trick.

The practical win is that the development loop stays responsive and proportionate to the change you made.

build --auto stays quiet.

It checks every 200 ms, writes the latest meaningful build output to .nift/build-auto.log only when that text changes, and strips terminal colour codes from the log. In an interactive terminal press q to stop.