Design philosophy
Why Nift?
Because a web project already has an enormous ecosystem. Nift does not need to replace it. It only needs to make composition, tracking and rebuilding simpler.
Browsers understand HTML, CSS and JavaScript. npm and Bun manage packages. TypeScript compiles types. Vite and bundlers build frontend assets. Go, Node and Python run servers. Databases store data. Hosting platforms deploy files. Nift can be useful without pretending those things need a Nift-specific replacement.
The four jobs at the centre
Track
Give the outputs you care about stable tracked names and let Nift know which content/template relationship produces them.
Compose
Put shared structure in templates and partials. Put page-specific markup in content. Reuse the pieces without inventing a component runtime.
Resolve
Use project-aware paths and metadata instead of scattering fragile assumptions about directory depth throughout shared markup.
Rebuild
Record the dependency graph and rebuild the outputs affected by a change instead of treating every edit like a clean-room rebuild.
Adoption should not require surrender.
Some tools are most useful when the project becomes a project *of that framework*. Nift can be useful while remaining only one layer inside a repository.
your project
├─ HTML / CSS
├─ TypeScript / JavaScript
├─ React / Vue / Svelte (if useful)
├─ Vite / bundlers
├─ Go / Node / Python backend
├─ database / auth / APIs
├─ deployment tooling
└─ Nift
├─ tracked outputs
├─ templates / partials
├─ project-aware paths
└─ dependency-aware builds Ordinary web code is a feature.
A Nift page can remain HTML. A stylesheet can remain CSS. Browser behaviour can remain JavaScript. Your backend can remain Go. That makes projects easier to inspect with standard tools and reduces how much Nift-specific knowledge has to survive across years of maintenance.
Powerful simplicity helps humans.
A small surface is easier to remember, document, debug and teach. The important abstractions remain visible: this page uses this template; this template inputs this partial; this link points to this tracked output.
Human-first simplicity also produces strong AI-DX.
Nift is designed first for people: ordinary source files, explicit relationships, fast feedback and a small language. Those same properties happen to make projects easier for coding assistants to understand and validate without replacing the human-oriented model. See the AI-assisted workflow →
Prefer checked relationships over unchecked strings.
Nift is increasingly useful not because it knows more about every technology in your stack, but because it can make the relationships it does know about explicit. @input establishes an input/dependency relationship, @pathto checks project-local references, JSON and schemas validate structured data, and project contracts can make shared application values such as route names fail at build time when they drift.
This is the broader contract philosophy: if an important assumption is known at build time and Nift can check it simply and clearly, prefer the checked relationship to an unchecked string. The feature still has to justify its complexity; a theoretically verifiable guarantee is not automatically worth adding.
Read the contracts philosophy →
Built to be dependable.
Dependability is one of Nift's engineering values, and it is not the claim that software never fails. For Nift it means something narrower and more inspectable: the behaviour users are expected to rely on has explicit scope, known boundaries, and evidence behind it.
Nift applies that principle both to the projects it builds and to its own development. Dependencies are tracked rather than guessed. Important behaviour is expressed as contracts rather than left to inference. Regressions become permanent tests. Public reliability claims are tied to evidence, and the boundaries of that evidence are recorded rather than hidden. The same discipline shows up in nift init --handover, which generates a project handover so the assumptions a project depends on are written down and available to every future contributor — human or AI.
The goal is not to say trust us. It is to make as much of that trust as possible inspectable. Knowing where a guarantee stops is part of the guarantee: a system that says what is established, what the boundary is, and what the evidence looks like is more dependable than one making a broader claim with weaker justification.
Fast should mean more than benchmark numbers.
Nift is written in C++ and supports multithreaded builds, but the workflow advantage is especially visible in incremental development. Change one page and rebuild what changed. Change a shared partial and rebuild the pages that depend on it. Avoid repeatedly waking every part of a large toolchain for unrelated edits. See the reproducible 10,000-page clean-build evidence.
Peak RAM stayed between 9.6 and 11.3 MiB across these incremental cases.
Fast full builds
A native compiled build tool keeps overhead low when the project genuinely needs to rebuild everything.
Fast incremental builds
During normal development, the dependency graph lets the amount of work stay closer to the size of the change.
Small project footprint
Nift does not require a runtime dependency tree merely to turn templates/content into deployable HTML.
One native executable
The core tool is easy to reason about operationally: install Nift and point it at a project rather than bootstrapping a whole framework runtime.
What Nift deliberately does not try to own
Need TypeScript? use TypeScript.
Need React? use React.
Need Tailwind? use Tailwind.
Need image tooling? use image tooling.
Need a Go server? use Go.
Need Postgres/Auth? use Postgres / Supabase / your choice.
Need deployment? deploy public/ where it belongs. A useful feature must justify its permanent cost
Nift's small surface is not an accident and it is not a promise that the language can never grow. The test for a new feature is whether it solves a recurring build-time problem while preserving a compact mental model. Structured JSON, loops and conditions pass that test because they let data describe static output. That does not imply that mutable variables, arbitrary computation or a general-purpose scripting runtime belong in the core.
Prefer build-time knowledge
If the value is already known while building, Nift can turn it into ordinary output rather than requiring the browser to reconstruct it later.
Prefer immutable data flow
Load data, inspect it and render from it. Avoid hidden mutable state that makes templates depend on execution history.
Prefer explicit relationships
Tracked names, inputs, paths and dependencies should make it possible to explain why an output exists and why it rebuilt.
Prefer composition over replacement
A specialised external tool is often better than growing a Nift-specific substitute for functionality the wider ecosystem already solves well.
Static generation does not mean static experiences
Nift decides what can be known before deployment. The browser can still own what genuinely belongs at runtime. A product grid, navigation tree or documentation index can be generated from JSON with loops and conditions; a modal, live search box, WebSocket feed or interactive dashboard can use ordinary JavaScript or a component framework. Keeping those phases distinct avoids shipping runtime machinery merely to produce markup that was already knowable at build time.
Compatibility should be quiet, not contagious
Where practical, Nift can keep a narrow compatibility path for existing projects without making historical aliases or obsolete concepts part of the recommended language. New documentation should teach the smallest current model. Compatibility exists to reduce migration pain, not to dictate the shape of future projects.
The result
Nift provides the glue without trying to become the universe.
That is not an excuse for missing features. It is the boundary: Nift should be excellent at the small set of project-aware build problems it owns, and easy to compose with specialist tools for everything else.