NIFT LABSCase file / 08All experiments ↗

Architecture investigation / 08 / October 2026

TanStack.comTwo attempts.
Closed.

Exploring where Nift fits alongside TanStack.
Not replacing the entire ecosystem.

We wanted Nift to own publication and build work while keeping the TanStack libraries and application behavior that make sense to retain. The faithful migration retained too much expensive Start/runtime work. A subsequent rewrite improved separation, but its chosen corpus-generation and dependency architecture failed the performance gate. This was never intended as “replace TanStack with Nift.”

Independent research. No TanStack affiliation or endorsement. No Nift core changes.

Two attempts / distinct termination reasons
01 / MIGRATION

Retained
runtime

Start · browser · Worker

Not enough dominant work moved
NO COMPELLING ARCHITECTURE
02 / REWRITE

Generated
corpus graph

Preparation · contexts · dependencies

Chosen representation too expensive
Finding 01

Migration: retained runtime too dominant. Rewrite: the particular generated corpus and dependency graph were too expensive. Neither establishes a general Nift limitation.

01

boundary

Phase 1 — faithful migration.

The target was the full public application, not a static imitation of its docs. Request-specific sessions, Router/Query behavior, real charts, search, chat/API boundaries and generated products remained part of the contract.

Build responsibility and request-time ownership

Upstream

Original content inputs

TanStack Start / Vite

React / Redact / Router / Query
Cloudflare application services

Start owns request-time HTML

A / faithful Nift

Nift publication
Docs + projections + assets

Retained Start / Vite build

Original browser + Worker runtime

Dominant build cost remains

B / alternate engine

Same Nift publication

Official Start / Rsbuild adapter

Original browser + Worker runtime

Lower RSS / slower warm full

B changes the compiler engine. It does not transfer request-time page or docs rendering to Nift.

Nift tanstackAuthored-source / faithful

Maintained Markdown, frontmatter and structured references derive document AST, raw/download views and metadata. Nift composes prepared inputs; Start renders the public application.

Nift tanstack-agentRendered-source / agent-primary

Maintained TanStack document AST envelopes with explicit original/download projections. This is a hybrid runtime-consumed representation, not whole maintained HTML. More restructuring freedom did not relax parity.

React, Router, Query, Table and AI remain product dependencies where used. Start also supplies server functions, serialization and hydration. Vite is build tooling, not a required runtime library. Redact aliases, Tailwind, Content Collections, Sentry runtime and Cloudflare bindings remain explicit integration responsibilities.

02

paths

Five branches. No forced winner.

  1. A

    Faithful Start / Vite

    Accepted behavior. Preserved as the conservative default, but not compelling as a complete application migration.

    Preserved
  2. B

    Official Start / Rsbuild adapter

    Executable alternate engine. Memory falls; warm full publication slows. Start stays in charge of SSR.

    Tradeoff
  3. C1

    Whole-page snapshot + real backend

    Required request-specific session behavior is frozen in the published HTML.

    Rejected
  4. C2

    Maintained article HTML bridge

    HTML-only edits appear on the server, then disappear when the original AST-based components mount.

    Rejected
  5. R

    Nift-first rewrite

    Separation worked. The chosen generated publication graph failed its viability gate.

    Closed

Plain esbuild also failed with 204 browser-graph compilation errors: server-only imports and framework transformations cannot simply be skipped. Failed compilation and rejected C branches receive no successful-build timing.

03

parity

The migration failure was architectural.

Preserving behavior worked. Finding a worthwhile new ownership boundary did not.

7,527document contracts / 25 roots
333browser states / no page errors
24,224files / normal–forced byte equality

The Rsbuild candidate reproduced with independently installed frozen runtime dependencies and a separately locked adapter toolchain. All 244 maintained static assets match byte-for-byte. Required type, lint, unit, chat and desktop suites pass, with one existing desktop skip.

Real local Worker proofs preserve API boundaries, generated output, byte-identical request-time OG PNGs, fresh sessions, Markdown negotiation, mobile navigation, search navigation and the catalog renderer. No handler was replaced with a static success stub.

Browser semantics and interaction checks are not universal pixel identity. API/service proofs use controlled fixtures and unauthenticated boundaries; private live authenticated integrations are not certified. Hosted Algolia remains; there is no omitted local search-index build.

04

earlier result

Keep the first faithful result visible.

The preserved T10 campaign has 225 observations: 15 cases × three implementations × five samples. It predates the T11 cache work and the T12 architecture comparison below. These cohorts must not be spliced into a single ranking.

T10 / seconds, median [min–max]Upstream Start / ViteNift authored-sourceNift rendered-source
Warm full31.2228.73–36.2632.4232.12–36.3128.9528.72–30.42
Fresh application state24.3124.16–24.5033.0932.72–34.8732.4529.14–32.58
Unchanged command32.1131.00–33.937.046.76–7.856.215.66–6.57
One document2.101.98–2.186.486.11–9.245.695.43–5.96
Route rename8.728.42–9.004.224.15–4.264.033.99–4.07
Route delete8.608.56–8.904.234.21–4.574.434.20–4.64

Unchanged and rename/delete publication improved; ordinary upstream document edits remained competitive or faster. Upstream changed-doc timing is its native ready-input R2 refresh, whereas Nift measures complete document/assets publication. This is production work, not development-server/HMR latency. Fresh-state cleanup placement also differs; the original report keeps those boundaries visible.

The T11 replacement 225-row campaign is still pending. Its optimized authored timings are not inferred from the agent-only T12 results.

05

measurements

Less memory. More time. Still Start.

The bounded T12 investigation compares two agent-source pipelines: faithful Start/Vite and official Start/Rsbuild. It is not a comparison of authored versus rendered source.

Warm full / complete publication
02550 s

Time

A / faithful Start + Vite32.94 s
B / Start + Rsbuild41.55 s
Warm full / maximum individual process RSS
02,5005,000 MiB

Memory

A / faithful Start + Vite4,507.6 MiB
B / Start + Rsbuild3,715.7 MiB

Rsbuild saves memory here, but loses warm-full time. Neither removes the dominant runtime build. This is the observed tradeoff in this application, not a judgement about Rsbuild generally.

T12 / seconds, median [min–max]A / faithful Start + ViteB / Start + Rsbuild
Warm full32.9430.50–36.8341.5540.10–43.20
Fresh application state46.3039.92–49.8144.5342.50–57.54
Unchanged command2.612.56–3.042.892.74–3.18
One document2.542.53–2.622.742.67–2.95
Shared shell32.1230.35–32.6535.0535.01–37.14
Browser component34.6130.52–37.5235.1034.33–35.91
Route rename3.593.55–3.733.273.21–3.62
Route delete3.823.26–4.463.893.54–5.37

80 accepted samples: eight workloads × two variants × five serialized repetitions. Clock includes required browser and backend bundling, content, assets and Worker configuration. No separate service build is hidden. Fresh-state ranges overlap substantially; no fresh-build winner is claimed. Installed dependencies and acquisition are excluded; fresh application state does not mean cold OS caches. Fixed cohort order and shared-host load limit small-difference claims.

06

incremental memory

The boundary is visible in iteration memory.

Document-only edits and lifecycle updates reuse verified runtime bundles. Shared shell and browser-component edits cross the retained compiler boundary. Peak memory rises with that boundary, not with Nift simply rendering one page.

Changed-input / maximum individual process RSS
02,5005,000 MiB

Unchanged command

A / faithful Start + Vite301.1 MiB
B / Start + Rsbuild321.6 MiB

One document

A / faithful Start + Vite301.1 MiB
B / Start + Rsbuild319.3 MiB

Shared shell

A / faithful Start + Vite4,512.1 MiB
B / Start + Rsbuild3,824.7 MiB

Browser component

A / faithful Start + Vite4,524.9 MiB
B / Start + Rsbuild3,710.2 MiB

Route rename

A / faithful Start + Vite303.7 MiB
B / Start + Rsbuild321.2 MiB

Route delete

A / faithful Start + Vite302.8 MiB
B / Start + Rsbuild319.1 MiB
T12 / MiB, median [min–max]A individual processB individual processA sampled treeB sampled tree
Warm full4,507.64,471.2–4,562.63,715.73,613.6–3,754.85,124.05,077.5–5,192.63,867.23,777.1–3,916.8
Fresh application state4,516.34,406.6–4,559.63,876.83,861.6–3,931.95,161.75,007.9–5,197.04,038.94,018.8–4,095.0
Unchanged command301.1299.1–302.8321.6316.6–325.6456.2453.4–461.4474.0470.1–478.7
One document301.1298.3–308.3319.3317.1–327.0454.4452.5–466.4477.9477.4–484.5
Shared shell4,512.14,425.2–4,548.03,824.73,745.1–3,846.85,115.55,034.5–5,155.73,979.33,892.2–4,009.7
Browser component4,524.94,450.8–4,612.33,710.23,626.0–3,780.95,135.25,063.0–5,190.53,869.93,788.7–3,936.4
Route rename303.7301.4–311.4321.2318.6–327.8458.8455.3–463.7483.0481.3–485.8
Route delete302.8299.8–303.9319.1318.4–322.6457.9453.9–458.3478.5475.5–484.8

Individual RSS is GNU time maximum process/phase usage, not aggregate memory. Tree RSS is a nominal 50ms PID-deduplicated descendant sum: it can miss short peaks and double-count shared mappings. It is not PSS. Both scopes and observed ranges remain separate; neither is a dedicated-machine memory guarantee.

07

dominant cost

Nift did not consume 4.4 GiB to render pages.

Nift-owned work

Prepared docs / projections / composition

Useful publication work. Not the dominant full-build cost.

Migration boundary
Retained application compiler

Start + browser/server emission

Expensive runtime build responsibility remains here.

Warm full component / median secondsA / Start + ViteB / Start + Rsbuild
Retained engine, both environments27.9936.14
Complete content stage4.264.82
Nift composition within content stage0.840.94
Retained asset publication0.140.17
Publisher compilation0.350.40

Vite's approximately 4.4 GiB process peak occurs around server emission. V8 live heap was much smaller at the sampled RSS peak, and heap caps did not materially remove that RSS peak. This does not establish a native allocator cause; overlapping diagnostic heap trials are not accepted timing comparisons. Rsbuild's own Node compiler becomes dominant around SSR completion.

This does not show that Nift itself has the same build cost or memory profile as TanStack Start. It shows that the migration retained too much of the existing runtime/build architecture for Nift to meaningfully change the dominant cost.

Nift composition is a nested component, not an additional cost to sum on top of content publication. Median components need not sum to a median total. These are complete orchestrated pipelines, not native @markup performance.

08

rejected splits

The deeper branches ended at real contracts.

C1 / branch terminated

A live backend cannot unfreeze a static shell.

Original backend forwarding worked and its request seed rotated. The Nift-published whole page retained the old seed. Required partner/session behavior therefore failed. Static output alone was not a faithful substitute.

C2 / branch terminated

Server-visible HTML was not the maintained authority.

Three real pages covered ordinary content, a table and tabs/code. Twelve scoped browser states initially matched. An HTML-only edit published normally and appeared in SSR, then vanished after the original interactive components mounted from their unchanged AST. A shadow AST would require coordinated maintenance; a clean HTML authority needs a different component/island boundary.

We stopped rather than weaken parity, fake dynamic functionality or reimplement substantial parts of Start. These bounded failures do not prove that every possible service/island design is impossible.

09

what worked

Useful work. An unconvincing migration.

  • Verified bundle reuseUnchanged publication avoids redundant application compilation.
  • Docs stay outside the heavy buildNormal document edits reuse the unchanged browser/backend bundles.
  • Correct publication and retirementCustom publisher, explicit dependencies, rename/delete handling and cache repair preserve outputs.
  • Independent recomputationNormal and forced output equality survives a fresh dependency install.

Those gains matter, but they do not make the entire application migration worthwhile. Full/fresh build cost, retained peak memory and compiler integration complexity remain. The source cache improvements do not transfer request-time HTML ownership.

Why publish a negative result?

Labs records what actually happens, including targets where migration is not compelling. Publishing the failed boundary makes the successful experiments more credible and keeps architecture fit separate from benchmark cherry-picking.

10

rewrite

Phase 2 — Nift-first rewrite. Closed.

Initialized with nift init --rewrite, this attempt moved static HTML and publication onto Nift while retaining useful TanStack components, frontend islands and explicit backend services.

Attempt 1 / terminatedMigration
Retained runtime dominates
Attempt 2 / terminatedRewrite
Generated corpus too costly

Nift-owned HTML remained authoritative and retained runtime pieces continued to work. Exact design and behavior parity was pursued. The resulting implementation still failed its agreed viability gate after one bounded performance pass; the experiment closed without further rescue or parity polishing.

Workload distinction: the rewrite emits 14,247 complete static pages plus 14,107 fragments, while upstream and the faithful migration serve docs dynamically. These differ in responsibility. That difference does not waive the agreed viability gate.

Accepted full-publication medians / separate cohorts and responsibilities
0100.0200 s
Upstream / accepted T1031.22 s
Faithful migration / accepted T1032.42 s
Rewrite / three-sample decision gate192.28 s
Rewrite decision gate / secondsMedianObserved range
Warm full192.28187.08–194.47
Unchanged10.369.92–11.06
Docs edit37.5037.03–40.59
Frontend edit163.69148.03–181.07

Three serialized samples per scenario; diagnostic viability evidence, not a final five-sample acceptance campaign. Accepted upstream/migration measurements were not rerun. Fresh-state, shared-layout and backend-only cases were not rerun after the hard full-build gate failed.

11

rewrite memory

Two memory scopes. Keep them separate.

Rewrite full / maximum individual process or phase RSS
03000.06000 MiB
Maximum individual process / phase2,378.90 MiB
Rewrite full / sampled descendant tree RSS
03000.06000 MiB
Sampled tree / separate scope5,364.90 MiB
Rewrite incremental / maximum individual process or phase RSS
03000.06000 MiB
Unchanged114.18 MiB
Docs edit1,525.68 MiB
Frontend edit2,216.05 MiB

These are different metrics and should not be read interchangeably. Individual-process RSS improved substantially from the earlier rewrite, but sampled tree memory remained high. The bounded pass changed warm full from 232.01s to 192.28s, docs edits from 63.40s to 37.50s and frontend edits from 482.51s to 163.69s. Unchanged publication regressed from 5.79s to 10.36s.

GNU time maximum process/phase RSS is not aggregate memory. Sampled descendant RSS can double-count shared mappings and miss short peaks; it is not PSS. Persistent preview/database services are excluded.

12

rewrite proofs

What worked. What remained too expensive.

96/96semantic states passed
91/96exact RGB states passed
14,247complete static pages
  • Five thin viewport differencesPreserved as open visual items, not final parity acceptance.
  • 14,107 docs outputsFrontend edits rerendered zero docs; docs edits affected six docs. Browser/backend builds stayed off for docs edits.
  • Subsystem separationNift-owned HTML, selective retained runtime pieces and frontend/backend boundaries worked.
  • 4.72 GB retired cachesMapped repeated uncompressed shell caches were removed after equivalence checks.
Warm-full phase / median secondsCost
Frontend producer26.91
Corpus preparation36.10
Exclusive Nift work129.12

Exclusive Nift work includes dependency processing, caching, composition, output and orchestration for this generated graph. It is not a measure of ordinary Nift template rendering or built-in Markdown performance. Producer phases overlap; component medians need not sum to a median total.

The rewrite successfully separated Nift-owned publication from retained runtime services, but its generated corpus representation and preparation pipeline were too expensive to justify. Whole-corpus page-context traversal/serialization persisted, and fine-grained shared imports increased dependency/composition work.

The chosen corpus-generation and dependency architecture failed. This does not establish that a simpler Nift template/content architecture would have the same cost.

No general Nift core ceiling was demonstrated. A simpler shared-template approach was not disproved; the experiment stopped because its one authorized rescue pass missed the agreed gate.

13

lessons

Guidance for the next rewrite, elsewhere.

Preserving too much incumbent architecture can obscure Nift’s benefits; architectural freedom alone does not guarantee a better result. Staticizing a large dynamic-docs surface introduces real responsibility and cost.

  • Require corpus-scale generation and usable publication coverage early, rather than isolated parity proofs.
  • Run a complete-workload performance viability gate before extended parity refinement.
  • Require explicit frontend, backend and content invalidation boundaries.
  • Use dependency-aware preparation and a bounded shared-chrome strategy.
  • Make @input, @content and @path guidance prominent; use @path for internal links, images, video and other local publication references.
  • Prefer ordinary shared templates and small per-page content/metadata when they meet the contract; retain islands where their functionality earns the complexity.

Bounded shared templates, grouped immutable chrome data, avoiding unchanged context reserialization and profiling large dependency graphs remain separate Nift/tooling research candidates. They do not reopen this experiment.

14

evidence

Inspect the result. Keep the boundaries.

This page closes both the migration and rewrite as unsuccessful. Rewrite measurements use Nift 4.10.0 and Node 25.9.0; the migration toolchain below belongs to its separate cohort. It uses the accepted T12 investigation and keeps T10 historical evidence separate. The broader T11 replacement 225-row campaign is not complete and is not used to overstate the conclusion. Repository checkpoints were clean and synchronized at publication; frozen links preserve the exact source/evidence state.

Toolchain: Nift 4.9.0, Node 25.9.0 and pnpm 11.1.0. Raw logs, environment/sampler records, restoration receipts and rejected experiments remain in the experiment repositories. Labs stores compact rendering summaries and displayed assets only.

15

conclusion

Neither tested architecture is recommended.

We would not recommend either tested TanStack.com architecture.

Migration

Too much expensive incumbent runtime/build architecture remained; Nift did not take ownership of enough dominant work.

Rewrite

Separation improved, but the particular generated corpus representation, preparation pipeline and dependency graph built for this rewrite remained too expensive.

This establishes neither that Nift is generally unsuitable for TanStack projects nor that ordinary shared templates and content would incur the same cost. It establishes that neither tested implementation was compelling for this application and workload.

Labs publishes unsuccessful experiments as well as successful ones. The purpose is to learn where Nift is useful, not manufacture a win on every target.

Migration: unsuccessfulRewrite: unsuccessful / experiment closed