Skip to content

Commit 20391ea

Browse files
Add website documentation for TanStack Redact (#46)
1 parent becad73 commit 20391ea

10 files changed

Lines changed: 321 additions & 0 deletions

‎README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ The goal is React's APIs and everyday behavior without concurrent scheduling, no
66

77
These APIs are included in Redact 0.1.0. Comparisons use pinned React 19.3.0 and were recorded September 11, 2026. The benchmark reports retain the exact measured source snapshots and build inputs.
88

9+
[Documentation](https://tanstack.com/redact/latest/docs/overview) · [Website](https://tanstack.com/redact/latest)
10+
911
## Quick start
1012

1113
```bash

‎docs/compatibility.md‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
title: Compatibility
3+
---
4+
5+
This is the default Vite configuration. API availability is not a promise of every React behavior, especially concurrent behavior.
6+
7+
| Area | Redact behavior |
8+
|---|---|
9+
| Core rendering | JSX, hooks (including `use`, `useEffectEvent`, `useId`, and `useSyncExternalStore`), context, refs, memo, lazy, portals, error boundaries, and modern class lifecycles. Legacy class lifecycles are no-ops. |
10+
| DOM and forms | Controlled inputs, reset defaults, event handling, and preservation of edits during hydration have shared React tests. |
11+
| Suspense and hydration | Fallbacks, retained primary DOM, retries, streaming boundaries, and event replay. No priority scheduling. |
12+
| `Activity` | Retains hidden DOM/state and disconnects effects, refs, and subscriptions. Insertion effects stay connected. Hidden work is synchronous, not background priority. |
13+
| Fragment refs | Stable DOM instances with events, focus, geometry, observers, scrolling, and portal/visibility tracking. |
14+
| Transitions | `useTransition` / `startTransition` run work synchronously. Pending stays false; `useDeferredValue` returns its input. No time slicing or interruptible renders. |
15+
| `useActionState` | Returns the initial state, a no-op dispatch, and `false`. It does not run the action. |
16+
| `useOptimistic`, `useFormStatus` | No optimistic overlay; the optimistic setter is a no-op. Form status stays idle. |
17+
| View transitions | `ViewTransition` preserves state; `addTransitionType` does not animate by default. Native snapshots/styles/callbacks are experimental and opt-in. Animated commits wait for the browser snapshot callback, without a concurrent scheduler. |
18+
| Browser-only rendering | `browser()` requests a server-to-client Suspense fallback; `onBrowserBailout` reports it separately from errors. `use(browser())` returns `undefined` on the client. |
19+
| Resource APIs | Preload/preinit/preconnect/DNS hints and declarative styles/scripts, with document/ShadowRoot ownership and streamed stylesheet coverage. |
20+
| SSR | `renderToString`, `renderToStaticMarkup`, readable and pipeable streams. Full Fizz prerender/resume APIs are not implemented. |
21+
| Caching | `cache` returns its function, `cacheSignal` returns `null`, and `unstable_useCacheRefresh` is a stable client no-op. Invoking a server refresh throws. Client/DOM-server rendering is uncached. |
22+
| React Compiler | The `react/compiler-runtime` memo-cache entrypoint is implemented and aliased. This is not validation of every compiler output pattern. |
23+
| Server Components / Flight | Kept on upstream React in the RSC environment, not reimplemented by Redact. |
24+
| Debugging | Fast Refresh works with `@vitejs/plugin-react`, preserving state for compatible edits and remounting when hook signatures change. `StrictMode` / `Profiler` render children without double invocation or profiling. `useDebugValue` is a no-op. React DevTools inspection is not implemented. |
25+
26+
Native animation is not exhaustive visual parity. Production animation, hidden-tab behavior, broader resource/navigation races, and Safari/Firefox remain unverified. [API details](https://github.com/TanStack/redact/blob/main/docs/REACT_19_3_SUPPORT.md), [upstream audit](https://github.com/TanStack/redact/blob/main/docs/REACT_19_3_AUDIT.md), [native animation limits](https://github.com/TanStack/redact/blob/main/docs/VIEW_TRANSITION_EXPERIMENT.md).

‎docs/config.json‎

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{
2+
"$schema": "https://raw.githubusercontent.com/TanStack/tanstack.com/main/tanstack-docs-config.schema.json",
3+
"sections": [
4+
{
5+
"label": "Getting Started",
6+
"children": [
7+
{
8+
"label": "Overview",
9+
"to": "overview"
10+
},
11+
{
12+
"label": "Installation",
13+
"to": "installation"
14+
},
15+
{
16+
"label": "Quick Start",
17+
"to": "quick-start"
18+
}
19+
]
20+
},
21+
{
22+
"label": "Guides",
23+
"children": [
24+
{
25+
"label": "Compatibility",
26+
"to": "compatibility"
27+
},
28+
{
29+
"label": "Feature Flags",
30+
"to": "feature-flags"
31+
},
32+
{
33+
"label": "Bundle Size and Performance",
34+
"to": "performance"
35+
},
36+
{
37+
"label": "Testing Your Integration",
38+
"to": "verification"
39+
}
40+
]
41+
},
42+
{
43+
"label": "Reference",
44+
"children": [
45+
{
46+
"label": "Vite Plugin",
47+
"to": "vite-plugin"
48+
}
49+
]
50+
}
51+
]
52+
}

‎docs/feature-flags.md‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
title: Feature Flags
3+
---
4+
5+
`redact()` uses the `full` preset: all optional features enabled **except native animation**. Turn off only behavior your app does not need:
6+
7+
```ts
8+
redact({ features: { hydration: false, classComponents: false } })
9+
redact({ features: { viewTransitions: true } }) // experimental animation
10+
redact({ preset: 'nano', features: { context: true } })
11+
```
12+
13+
`nano` starts with every optional feature off. Overrides merge with the preset. Feature savings overlap and are not additive.
14+
15+
<details>
16+
<summary>What each disabled feature removes</summary>
17+
18+
| Flag | Behavior when `false` |
19+
|---|---|
20+
| `activity` | Hidden children unmount and lose state. |
21+
| `fragmentRefs` | No Fragment DOM instance. |
22+
| `viewTransitions` | No native snapshots or animation callbacks; the boundary retains state. |
23+
| `portal` | Children render in place, not into the target container. |
24+
| `context` | Providers do not propagate values; reads return the default. |
25+
| `suspense` | No fallback UI; thrown thenables still retry on settlement. |
26+
| `memo` | No prop-equality bailout. |
27+
| `forwardRef` | No wrapper forwarding; ordinary ref props still work. |
28+
| `lazy` | Only already-synchronous payloads resolve. |
29+
| `classComponents` | Constructor, render, and setState only; no lifecycles or error boundaries. |
30+
| `hydration` | `hydrateRoot` throws; use `createRoot`. |
31+
32+
</details>

‎docs/installation.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
title: Installation
3+
---
4+
5+
```sh
6+
pnpm add @tanstack/redact
7+
```
8+
9+
Keep your existing React dependencies and framework plugins. Your application continues importing from `react` and `react-dom`, the Vite plugin redirects those imports when it builds your app.
10+
11+
Add the plugin as described in [Quick Start](./quick-start.md). No application-wide import rewrite is needed.

‎docs/overview.md‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
title: Overview
3+
---
4+
5+
React, redacted. A small React-compatible runtime with synchronous rendering. One Vite plugin replaces the React, DOM, server, scheduler, and JSX entrypoints, your application's imports stay unchanged.
6+
7+
Redact keeps the React component model and everyday APIs without concurrent scheduling. JSX, hooks, context, refs, memo, lazy, portals, error boundaries, streaming SSR, and hydration are supported, but API availability does not mean every React behavior is implemented.
8+
9+
Read [Compatibility](./compatibility.md) before switching an existing app, especially if it depends on pending transitions, actions, optimistic state, or React DevTools.
10+
11+
## Get started
12+
13+
[Install Redact](./installation.md), then [add the Vite plugin](./quick-start.md). Start with the default preset and only disable features after checking what your app uses.
14+
15+
The [feature flags guide](./feature-flags.md) explains the behavior each flag removes. The [Vite plugin reference](./vite-plugin.md) covers monorepos and manual package resolution.
16+
17+
## Measurements
18+
19+
The [bundle size and performance guide](./performance.md) records the measured versions, configurations, and workload limits. Runtime bundle sizes are not whole-app sizes, and microbenchmarks do not establish whole-app speedups.

‎docs/performance.md‎

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
---
2+
title: Bundle Size and Performance
3+
---
4+
5+
These measurements describe the 0.1.0 release and its frozen pre-release timing snapshot, compared with React 19.3.0 on September 11, 2026. They are not measurements of every later release.
6+
7+
## Bundle size
8+
9+
Production ESM bundles retaining all exports from the React, JSX, DOM, and DOM-client entries:
10+
11+
| Runtime | Gzip bytes | Compared with React |
12+
|---|---:|---:|
13+
| React 19.3.0 | 69,162 | Reference |
14+
| Redact, default Vite configuration | 23,311 | 66.3% smaller |
15+
| Redact, native animation enabled | 28,159 | 59.3% smaller |
16+
17+
These are runtime bundles, not whole applications. Redact is measured from the [0.1.0 release build](https://github.com/TanStack/redact/blob/main/benchmarks/results/release-0.1.0-sizes.json). Real apps tree-shake differently, and the runtimes have different scheduling capabilities.
18+
19+
Standalone Redact entries, measured separately:
20+
21+
| Entry / configuration | Gzip bytes |
22+
|---|---:|
23+
| DOM client, default Vite features | 20,139 |
24+
| DOM client, native animation enabled | 24,991 |
25+
| DOM client, `nano` preset | 12,466 |
26+
| React API entry | 2,764 |
27+
| Server entry | 8,964 |
28+
29+
These entries overlap, so their sizes are not additive. `nano` removes behavior and is not a full-compatibility preset. The expanded API surface is larger than earlier Redact, not a size reduction over previous releases. [Exact configurations, inputs, and historical comparisons](https://github.com/TanStack/redact/blob/main/benchmarks/SHIP_RESULTS.md#size).
30+
31+
## Performance
32+
33+
Production microbenchmarks on an Apple M5 Pro, Chrome 152, without CPU throttling: five fresh-browser blocks, five rounds each. Redact uses the built package with default Vite features. Synchronous flushes ensure updates complete before timing stops.
34+
35+
These timings use the frozen pre-release snapshot, before the final hydration-recovery cleanup was merged from 0.0.21. That cleanup passed the release tests; the timing experiments were not repeated.
36+
37+
Times are median averages per completed workload batch in **milliseconds**, not per component. Negative percentages mean less time. Percentages use paired measurements, not division of the rounded medians.
38+
39+
| Browser workload | React ms | Redact ms | Redact time vs React |
40+
|---|---:|---:|---:|
41+
| Sparse state updates | 0.009 | 0.002 | -71.9% |
42+
| Mixed-depth state updates | 0.227 | 0.093 | -59.1% |
43+
| Keyed list reversal | 0.161 | 0.112 | -30.3% |
44+
| Context through memo | 0.073 | 0.052 | -29.1% |
45+
| Deep-tree prop updates | 0.012 | 0.010 | -22.4% |
46+
| Batched state updates | 0.050 | 0.041 | -19.7% |
47+
| Null-heavy child lists | 0.094 | 0.083 | -14.2% |
48+
| Stable keyed rows, automatic JSX | 0.050 | 0.043 | -11.6% |
49+
| Controlled selects | 0.142 | 0.132 | -7.6% |
50+
| Prop updates, automatic JSX | 0.237 | 0.226 | -3.8% |
51+
| Prop updates, classic JSX | 0.249 | 0.243 | -2.3% |
52+
| Stable keyed rows, classic JSX | 0.045 | 0.046 | No clear difference |
53+
| Mount and unmount | 0.149 | 0.162 | +7.5% |
54+
| Reducer batches | 0.051 | 0.055 | +7.9% |
55+
| String SSR in the browser | 0.038 | 0.043 | +10.4% |
56+
| Hydration | 0.182 | 0.227 | +21.4% |
57+
| Passive effects | 0.024 | 0.032 | +27.0% |
58+
| Reducer updates under Suspense | 0.051 | 0.076 | +49.6% |
59+
| Suspense retry cycles | 0.117 | 0.268 | +124.8% |
60+
61+
The largest ratio, Suspense retries, is about **0.15 ms of extra work per cycle** here. A cycle covers 96 components, 192 reducer actions, hiding, retrying, revealing, and shared correctness checks. It does not mean interactions feel 2.25 times slower. Larger trees, repeated retries, and slower devices can make the difference matter.
62+
63+
### Server rendering
64+
65+
Separate Node 24 measurements use production source bundles, five fresh-process blocks, and the same checked output. They exclude package startup, network latency, and streaming waterfalls.
66+
67+
| Server workload | React ms | Redact ms | Redact time vs React |
68+
|---|---:|---:|---:|
69+
| Fully-ready readable stream | 0.175 | 0.088 | -49.4% |
70+
| String SSR with hooks/context | 0.076 | 0.053 | -30.5% |
71+
| String SSR, clean text | 0.057 | 0.053 | -6.9% |
72+
| String SSR, escaped text | 0.126 | 0.124 | No clear difference |
73+
74+
### Interactions and memory
75+
76+
Separate 2,400-row fixtures use normal scheduling for clicks and forced garbage collection for retained heap:
77+
78+
| Measurement | React | Redact, default |
79+
|---|---:|---:|
80+
| Click handler to committed DOM, median / p95 | 1.5 / 1.6 ms | 1.2 / 1.3 ms |
81+
| Mounted retained JS heap, median | 666.4 KiB | 762.5 KiB |
82+
| Extra DOM nodes after final unmount | 0 | 0 |
83+
84+
Handler-to-commit is not paint latency or field INP; retained heap is not allocation rate or proof of no leaks. Observed click durations were browser-rounded to 16 ms for both; one Redact event entry was absent, not zero.
85+
86+
An identical-React control measured 0.5% median absolute variation across browser workloads, with wider uncertainty in some cases. Small differences need caution. These results do not establish whole-app speedups, behavior under concurrent input, or mobile/Safari/Firefox performance. [All intervals and measurements](https://github.com/TanStack/redact/blob/main/benchmarks/SHIP_MEASUREMENTS.md), [independent audit](https://github.com/TanStack/redact/blob/main/benchmarks/SHIP_AUDIT.md).

‎docs/quick-start.md‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
---
2+
title: Quick Start
3+
---
4+
5+
Add Redact to your existing Vite configuration. Keep your React or framework plugin in place.
6+
7+
```ts
8+
import { defineConfig } from 'vite'
9+
import react from '@vitejs/plugin-react'
10+
import { redact } from '@tanstack/redact/vite'
11+
12+
export default defineConfig({
13+
plugins: [react(), redact()],
14+
})
15+
```
16+
17+
Your application code stays familiar.
18+
19+
```tsx
20+
import { useState } from 'react'
21+
import { createRoot } from 'react-dom/client'
22+
23+
function Counter() {
24+
const [count, setCount] = useState(0)
25+
return (
26+
<button onClick={() => setCount(count + 1)}>
27+
Clicked {count} times
28+
</button>
29+
)
30+
}
31+
32+
createRoot(document.getElementById('root')!).render(<Counter />)
33+
```
34+
35+
The plugin handles client and SSR builds. The RSC environment stays on upstream React so `@vitejs/plugin-rsc` can own Server Component serialization.
36+
37+
## Check your app
38+
39+
Run your tests and production build. Check hydration, forms, portals, navigation, and third-party components. Pay particular attention to pending transitions, actions, and optimistic state, their behavior differs from React.
40+
41+
Read [Compatibility](./compatibility.md) before shipping. Keep hydration enabled in apps that use `hydrateRoot`.

‎docs/verification.md‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
title: Testing Your Integration
3+
---
4+
5+
Run your app's test suite and production build with the default Vite preset first. Check server-rendered routes on a direct load and after client navigation, then exercise forms, portals, Suspense boundaries, and third-party components.
6+
7+
Disabling hydration makes `hydrateRoot` throw. Disabling class components also removes class error boundaries and lifecycle methods. Read the feature-flag behavior before changing presets.
8+
9+
## Published verification
10+
11+
The following evidence describes the 0.1.0 release, it is not coverage of every application or later version.
12+
13+
`pnpm test:ci` passes 1,569 tests, type checking, the built-package verifier, and all 19 source/dist size budgets. The 91 ordinary-suite skips are not passes. Native Node import order, NodeNext declarations, development/production behavior, and actual Vite feature selection are checked.
14+
15+
The separate Chrome gate passes 1,266 case executions across Redact and React, including repeated suites/motion modes, not 1,266 unique behaviors. Seven declared exclusions remain: six Node-only stream executions and one known React Activity hydration failure. [Chrome ledger](https://github.com/TanStack/redact/blob/main/benchmarks/SHIP_CHROME_FINAL.md).
16+
17+
| Real app | Published Redact 0.1.0 result |
18+
|---|---|
19+
| tanstack.com | Production build, 503 tests, 5 local Worker routes, navigation/history, controls, portals, and mobile checks pass. Deployed preview works; production upgrade awaits required review. |
20+
| tannerlinsley.com | Upgraded in production. Normal build prerenders all 10 pages; 8 live routes and browser interactions pass with no browser errors. Existing mobile header overflow remains. |
21+
22+
These are integration checks, not site-speed measurements or coverage of authenticated flows. [Published-package and deployment evidence](https://github.com/TanStack/redact/blob/main/benchmarks/RELEASE_0_1_0_SITES.md), [earlier renderer controls](https://github.com/TanStack/redact/blob/main/benchmarks/SHIP_SITE_INTEGRATION.md).

‎docs/vite-plugin.md‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
title: Vite Plugin Reference
3+
---
4+
5+
```ts
6+
import { redact } from '@tanstack/redact/vite'
7+
8+
redact({
9+
preset: 'full',
10+
features: { viewTransitions: false },
11+
})
12+
```
13+
14+
## Options
15+
16+
| Option | Type | Behavior |
17+
| --- | --- | --- |
18+
| `preset` | `'full' | 'nano'` | Defaults to `full`. Full enables every optional feature except native animation. Nano disables all optional features. |
19+
| `features` | `RedactFeatures` | Boolean overrides merged with the preset. See [Feature Flags](./feature-flags.md). |
20+
| `skip` | `ReadonlyArray<string>` | Import specifiers to exclude from aliasing. |
21+
| `resolveFrom` | `string` | Package resolution root, defaults to the Vite config root. Useful in monorepos. |
22+
| `packageRoots` | `Record<string, string>` | Explicit package names mapped to absolute package directories, bypassing node_modules lookup for local builds. |
23+
24+
## Import aliases
25+
26+
The plugin redirects React, JSX runtimes, React Compiler runtime, React DOM, DOM client, server rendering, scheduler, and use-sync-external-store entrypoints to Redact. Your application's React imports stay unchanged.
27+
28+
The RSC environment remains on upstream React, Redact does not implement Flight serialization.
29+
30+
For the exact alias map and resolution behavior, see the [plugin source](https://github.com/TanStack/redact/blob/main/packages/redact/src/vite/index.ts). The [development guide](https://github.com/TanStack/redact/blob/main/docs/DEVELOPMENT.md) covers runtime internals.

0 commit comments

Comments
 (0)