[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-11781":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":13,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":15,"forks30d":15,"starsTrendScore":19,"compositeScore":20,"rankGlobal":10,"rankLanguage":10,"license":10,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":21,"hasPages":21,"topics":23,"createdAt":10,"pushedAt":10,"updatedAt":24,"readmeContent":25,"aiSummary":26,"trendingCount":15,"starSnapshotCount":15,"syncStatus":14,"lastSyncTime":27,"discoverSource":28},11781,"redact","TanStack\u002Fredact","TanStack","An alternative logical projection of React with 100% API compliancy but simpler implementation resulting in smaller bundle size and better performance.","",null,"TypeScript",116,6,2,0,3,4,37,9,2.54,false,"main",[],"2026-06-12 02:02:33","# redact\n\n**React, redacted.** A minimal React-19-API-compatible drop-in replacement, **~4× smaller** than canonical React. Shipped as a single `@tanstack\u002Fredact` package with subpath exports for the `react` \u002F `react-dom` \u002F `react-dom\u002Fserver` \u002F `scheduler` \u002F `react\u002Fjsx-runtime` shapes. User code keeps its canonical `import { useState } from 'react'` — the swap happens at the bundler level.\n\n- **9.07 KB** gzip at full drop-in parity (vs ~45 KB for React 19)\n- **6.75 KB** gzip with every opt-in feature stubbed (`nano` preset)\n- **707\u002F707** unit + integration tests passing, SSR + streaming Suspense + hydration included\n- Running in production on [tanstack.com](https:\u002F\u002Ftanstack.com) as of 2026-04-20\n\nFor background on the motivation, the \"projection\" framing, the architectural approach, and the production performance results, see the blog post: [Projecting React](https:\u002F\u002Ftannerlinsley.com\u002Fposts\u002Fprojecting-react).\n\n---\n\n## Quick start\n\n```bash\npnpm add @tanstack\u002Fredact@next\n```\n\n```ts\n\u002F\u002F vite.config.ts\nimport { defineConfig } from 'vite'\nimport { redact } from '@tanstack\u002Fredact\u002Fvite'\n\nexport default defineConfig({\n  plugins: [redact()],\n})\n```\n\nThat's it. The plugin aliases `react` \u002F `react-dom` \u002F `scheduler` across Vite's client + ssr environments. The RSC environment is skipped so `@vitejs\u002Fplugin-rsc` keeps using real React for Flight serialization. User-facing imports are unchanged:\n\n```ts\nimport { useState, Suspense } from 'react'\nimport { createRoot, hydrateRoot } from 'react-dom\u002Fclient'\n```\n\n### Shrink further with feature flags\n\nTwo presets — pick a starting point, flip flags from there:\n\n```ts\nredact({ preset: 'full' })        \u002F\u002F 9.07 KB — everything on, opt OUT individual features\nredact({ preset: 'nano' })        \u002F\u002F 6.75 KB — everything off, opt IN what you need\n```\n\nOpt out from `full`:\n\n```ts\nredact({\n  preset: 'full',\n  features: {\n    hydration: false,                  \u002F\u002F SPA only — no SSR\n    classComponents: false,            \u002F\u002F function components only\n  },\n})\n```\n\nOpt in from `nano`:\n\n```ts\nredact({\n  preset: 'nano',\n  features: {\n    context: true,                     \u002F\u002F bring back just what you need\n    suspense: true,\n  },\n})\n```\n\nFull feature matrix and alternative configuration paths below.\n\n---\n\n## Feature flags\n\n### Feature matrix\n\n| Flag | Full behavior | Stub behavior (when `false`) | Savings (gzip) |\n|---|---|---|---:|\n| `portal` | `createPortal` into alt container | Children render in place, `container` ignored | ~30 B |\n| `context` | Provider push\u002Fpop + consumer walk | Provider → Fragment; `useContext` returns default | ~80 B |\n| `suspense` | Boundary + fallback + streaming hydration | Suspense → Fragment; thenables retry on settle | **~640 B** |\n| `memo` | `shallowEqual` prop-equality gate | Passes through every parent render | ~80 B |\n| `forwardRef` | Ref forwarded to inner fn | Ref dropped (React 19 \"refs as props\" still works) | ~70 B |\n| `lazy` | Full hydration coordination | Sync-resolvable payloads work; async retries on settle | ~20 B |\n| `classComponents` | Full lifecycle + `contextType` + error boundaries | `constructor` + `render` + `setState` only | ~200 B |\n| `hydration` | SSR DOM adoption, streaming boundaries, scroll guard, event replay | `hydrateRoot` throws; use `createRoot` for SPA | **~1270 B** |\n\n**Always on** (irreducible core, ~6.7 KB gzip): fiber reconciler with keyed child diffing, host DOM mount\u002Fupdate, `useState` \u002F `useReducer` \u002F `useEffect` \u002F `useLayoutEffect` \u002F `useInsertionEffect` \u002F `useRef` \u002F `useMemo` \u002F `useCallback` \u002F `useId` \u002F `useSyncExternalStore` \u002F `use` (for thenables), native event binding, Fragments, StrictMode\u002FProfiler (aliased to Fragment), element creation + JSX runtime.\n\n### Presets\n\n| Preset | What's on | `react-dom\u002Fclient` gzip | Intent |\n|---|---|---:|---|\n| `full` (default) | all 8 features | **9.07 KB** | Drop-in React — opt OUT individual features you don't need |\n| **`nano`** | none | **6.75 KB** | Start minimal — opt IN individual features you need |\n\nTwo presets, not a spectrum: every app either wants most of React (start from `full`, opt out) or a tight bundle (start from `nano`, opt in). Per-feature overrides merge on top of preset defaults either way.\n\n---\n\n## Configuration\n\nFour ways to configure, depending on your bundler and ergonomics preference.\n\n### 1. Vite plugin (recommended)\n\n`@tanstack\u002Fredact\u002Fvite`'s `redact()` plugin. Covered in [Quick start](#quick-start) above. Full options:\n\n```ts\ninterface RedactOptions {\n  preset?: 'nano' | 'full'                         \u002F\u002F default: 'full'\n  features?: {\n    portal?: boolean\n    context?: boolean\n    suspense?: boolean\n    memo?: boolean\n    forwardRef?: boolean\n    lazy?: boolean\n    classComponents?: boolean\n    hydration?: boolean\n  }\n  skip?: ReadonlyArray\u003Cstring>                     \u002F\u002F don't alias these specifiers\n  resolveFrom?: string                             \u002F\u002F override package resolution root\n  packageRoots?: Record\u003Cstring, string>            \u002F\u002F explicit package paths\n}\n```\n\nThe plugin also handles Vite-specific wiring: `optimizeDeps.exclude` for the shim packages, `ssr.noExternal` so SSR bundles inline them, and an `enforce: 'pre'` hook ordering so the alias wins over other resolvers.\n\n### 2. Bundler aliases (Webpack \u002F Rollup \u002F esbuild \u002F …)\n\nThe package exposes every feature module as a `.\u002Ffeatures\u002F*` subpath export. Any bundler with a path-alias feature can redirect a feature's `index` to its `stub` to opt the feature out of the bundle.\n\n**Subpath layout:**\n\n```\n@tanstack\u002Fredact\u002Ffeatures\u002F\n  portal\u002F context\u002F suspense\u002F memo\u002F forward-ref\u002F lazy\u002F class\u002F hydration\u002F\n    index    ← re-exports from .\u002Ffull by default\n    full     ← real implementation\n    stub     ← graceful degradation\n```\n\n**Webpack example (stubs hydration + suspense):**\n\n```js\n\u002F\u002F webpack.config.js\nmodule.exports = {\n  resolve: {\n    alias: {\n      '@tanstack\u002Fredact\u002Ffeatures\u002Fhydration\u002Findex':\n        '@tanstack\u002Fredact\u002Ffeatures\u002Fhydration\u002Fstub',\n      '@tanstack\u002Fredact\u002Ffeatures\u002Fsuspense\u002Findex':\n        '@tanstack\u002Fredact\u002Ffeatures\u002Fsuspense\u002Fstub',\n    },\n  },\n}\n```\n\n**Rollup:**\n\n```js\nimport alias from '@rollup\u002Fplugin-alias'\n\nexport default {\n  plugins: [\n    alias({\n      entries: [\n        {\n          find: '@tanstack\u002Fredact\u002Ffeatures\u002Fhydration\u002Findex',\n          replacement: '@tanstack\u002Fredact\u002Ffeatures\u002Fhydration\u002Fstub',\n        },\n      ],\n    }),\n  ],\n}\n```\n\n**esbuild:**\n\n```js\nimport { build } from 'esbuild'\n\nawait build({\n  entryPoints: ['src\u002Fapp.tsx'],\n  bundle: true,\n  alias: {\n    '@tanstack\u002Fredact\u002Ffeatures\u002Fhydration\u002Findex':\n      '@tanstack\u002Fredact\u002Ffeatures\u002Fhydration\u002Fstub',\n  },\n})\n```\n\n**Gotchas:**\n\n- **On-disk folder names vs. config keys**: `forward-ref\u002F` ↔ `forwardRef`, `class\u002F` ↔ `classComponents`. When configuring aliases manually, match the on-disk folder.\n- **Single-instance requirement**: `@tanstack\u002Fredact` (and any subpath of it) must resolve to **one** installed copy in your app. Mixing source + dist, or two different tarballs, duplicates `ReactSharedInternals` and breaks hooks. The package's `ReactSharedInternals` is stashed on `globalThis` under a registered symbol as a defense-in-depth, but you should still aim for a single copy.\n- **Feature interdependencies**: Suspense's full implementation imports hydration helpers. If hydration is stubbed but Suspense is full, the Suspense feature uses hydration's no-op stubs (fine — you're not hydrating). Suspense stubbed + hydration full is also fine (streaming boundaries just won't render fallback UI because `Suspense` maps to Fragment).\n\n### 3. Prebuilt bundle presets (planned)\n\nNot yet shipped. The planned shape:\n\n```ts\nimport { createRoot } from '@tanstack\u002Fredact\u002Fdom\u002Fnano\u002Fclient'\n```\n\nZero bundler configuration; useful for script-tag usage, non-bundler Node tools, or users who just want the smallest install without thinking about it.\n\n**Why not yet:** the preset bundle would need its own self-contained `_all.js` built with the right stubs compiled in — stubs can't reliably overlay a module that registers full variants first (registration order matters, last-write-wins). We want to gather real Vite-plugin usage data before deciding which prebuilt configurations are worth publishing. Open an issue with your use case if this unblocks you.\n\n### 4. npm aliases (limited)\n\n`npm:` package aliases in `package.json` work for the top-level `react` mapping but **not** for subpaths — there's no spec-level way to point `react-dom` at a subpath like `@tanstack\u002Fredact\u002Fdom` purely via `package.json`. So this path only gets you partway:\n\n```jsonc\n\u002F\u002F package.json — works, but only swaps `react` itself\n{\n  \"dependencies\": {\n    \"react\": \"npm:@tanstack\u002Fredact@next\"\n  }\n}\n```\n\nAnything that imports `react-dom`, `react-dom\u002Fclient`, `react-dom\u002Fserver`, or `scheduler` will still resolve to the real React in `node_modules` unless your bundler can rewrite those specifiers — at which point you may as well use Path 1 (Vite plugin) or Path 2 (bundler aliases). This is a real trade-off of the single-package layout: the install side is simpler but the no-bundler workflow loses some flexibility versus a multi-package shim. If you need a no-bundler full swap, open an issue with your toolchain and we can publish individual `@tanstack\u002Fredact-dom`, `@tanstack\u002Fredact-server`, etc. compatibility re-export packages.\n\n---\n\n## Advanced: authoring custom features & bundler plugins\n\nIf you're extending the system, writing a bundler plugin for a tool without one, or just curious how the swap works — the internal API surface is exported from `@tanstack\u002Fredact\u002F_all`.\n\n### Registration primitives\n\nFeature modules self-register by calling these at module load:\n\n```ts\nimport {\n  registerRenderer,\n  registerTypeMatcher,\n  registerElementMarker,\n  type RenderFn,\n  type TypeMatcher,\n} from '@tanstack\u002Fredact\u002F_all'\n\n\u002F\u002F Install a renderer for a FiberTag. Later calls overwrite earlier ones —\n\u002F\u002F stubs exploit this order-dependence.\nfunction registerRenderer(tag: FiberTag, fn: RenderFn): void\n\n\u002F\u002F Add a type matcher. Iterated in registration order during fiber creation,\n\u002F\u002F after core checks (string → Host, REACT_FRAGMENT_TYPE → Fragment) and\n\u002F\u002F before the function-vs-class fallback.\ntype TypeMatcher = (type: any, marker: any) => FiberTag | null\nfunction registerTypeMatcher(m: TypeMatcher): void\n\n\u002F\u002F Extend the accepted $$typeof set for child normalization. Default:\n\u002F\u002F REACT_ELEMENT_TYPE, REACT_LEGACY_ELEMENT_TYPE. Portal adds REACT_PORTAL_TYPE.\nfunction registerElementMarker(sym: symbol): void\n```\n\n### Capability hooks\n\nCross-cutting concerns (thrown-thenable handling, context reads) install via `installCapability`:\n\n```ts\nimport { installCapability, type Capabilities } from '@tanstack\u002Fredact\u002F_all'\n\ninterface Capabilities {\n  handleSuspended: (fiber: Fiber, thenable: Promise\u003Cany>) => void\n  readContext: (fiber: Fiber, ctx: any) => any\n}\n\nfunction installCapability\u003CK extends keyof Capabilities>(\n  name: K,\n  fn: Capabilities[K],\n): void\n```\n\nDefaults when no feature installs an override:\n- `handleSuspended`: retry-on-settle (no boundary stack, no fallback)\n- `readContext`: returns `ctx._currentValue` with no provider-tree walk\n\nThe full Suspense feature installs a boundary-stack-based `handleSuspended`. The full Context feature installs a walking `readContext`.\n\n### Authoring a custom feature\n\n```ts\n\u002F\u002F my-feature\u002Ffull.ts\nimport {\n  FiberTag,\n  registerRenderer,\n  registerTypeMatcher,\n  reconcileChildren,\n  childrenToArray,\n  type Fiber,\n} from '@tanstack\u002Fredact\u002F_all'\nimport { SOME_SYMBOL } from '@tanstack\u002Fredact'\n\nfunction renderMyThing(fiber: Fiber, domParent: Node, anchor: Node | null): void {\n  \u002F\u002F your render logic\n}\n\nregisterTypeMatcher((_type, marker) =>\n  marker === SOME_SYMBOL ? FiberTag.SomeTag : null,\n)\nregisterRenderer(FiberTag.SomeTag, renderMyThing)\n```\n\n```ts\n\u002F\u002F my-feature\u002Fstub.ts\nimport { FiberTag, registerTypeMatcher } from '@tanstack\u002Fredact\u002F_all'\nimport { SOME_SYMBOL } from '@tanstack\u002Fredact'\n\n\u002F\u002F Stub: treat my-thing elements as Fragments (children render normally).\nregisterTypeMatcher((_type, marker) =>\n  marker === SOME_SYMBOL ? FiberTag.Fragment : null,\n)\n```\n\nPair with an `index.ts` (`export * from '.\u002Ffull'`) and let your bundler pick which to import.\n\n### Authoring a bundler plugin\n\nThe Vite plugin's core is two `resolveId` cases. Port this pattern to any bundler's resolve hook:\n\n```ts\n\u002F\u002F Case 1: short specifier from features\u002Findex.ts\n\u002F\u002F Matches `.\u002Fportal`, `.\u002Fcontext`, etc.\nif (importer matches \u002Ffeatures[\u002F\\\\]index\\.(ts|js)$\u002F) {\n  const name = id.match(\u002F^\\.\\\u002F([a-z-]+)$\u002F)?.[1]\n  if (name && flags[name] === false) {\n    return resolveFrom(`.\u002F${name}\u002Fstub`, importer)\n  }\n}\n\n\u002F\u002F Case 2: resolved-path match for hydration\n\u002F\u002F (imported from reconcile, root, suspense\u002Ffull, lazy\u002Ffull)\nif (flags.hydration === false && \u002F\\\u002Fhydration$\u002F.test(id)) {\n  const resolved = await resolve(id, importer)\n  if (\u002Ffeatures[\u002F\\\\]hydration[\u002F\\\\]index\\.(ts|js)$\u002F.test(resolved)) {\n    return resolved.replace(\u002Findex\\.(ts|js)$\u002F, 'stub.$1')\n  }\n}\n```\n\nReal implementation: [packages\u002Fredact\u002Fsrc\u002Fvite\u002Findex.ts](packages\u002Fredact\u002Fsrc\u002Fvite\u002Findex.ts).\n\n### Verifying your setup\n\nWhichever path you choose, check that stubbed features' full code isn't in your output. Use your bundler's analyzer (rollup-plugin-visualizer, Webpack's bundle-analyzer, etc.) and search for `features\u002F\u003Cname>\u002Ffull.js`. With `hydration: false`, you should NOT see `features\u002Fhydration\u002Ffull.js` or its imports (cursor machinery, event-replay, scroll-guard).\n\n---\n\n## Scope\n\n### Supported\n\n- React 19 element model, JSX (classic + automatic), Fragment, Suspense, Portal, Error boundaries, forwardRef, memo, lazy\n- Full hook surface: `useState`, `useReducer`, `useEffect`, `useLayoutEffect`, `useInsertionEffect`, `useMemo`, `useCallback`, `useRef`, `useContext`, `useSyncExternalStore`, `useId`, `useDeferredValue`, `useTransition`, `use` (Context + Promise), `useEffectEvent`\n- Class components with full lifecycle (`componentDidMount`\u002F`componentDidUpdate`\u002F`componentWillUnmount`, `contextType`, `shouldComponentUpdate`, `getDerivedStateFromError`, `componentDidCatch`, legacy lifecycles as no-ops)\n- SSR via `renderToString` \u002F `renderToReadableStream` \u002F `renderToPipeableStream` — including Suspense boundary streaming with `$RC` reveal + event replay\n- Hydration: SSR DOM adoption, deferred hydration for `use(promise)` \u002F lazy, cursor preservation across the synchronous `endHydration`\n- Cohabitation with `@vitejs\u002Fplugin-rsc`: the Vite plugin deliberately skips the RSC environment so Flight serialization stays on real `react-server-dom`\n\n### Best-effort \u002F subset behavior\n\n- `useTransition` \u002F `useDeferredValue` run synchronously — no priority scheduling\n- Scheduler shim is a no-op wrapper around microtasks\n- No time slicing, no lane-based work interruption\n\n### Out of scope\n\n- `react-server-dom-*\u002Fclient` Flight deserializer (TanStack Start uses its own seroval-based codec + `@vitejs\u002Fplugin-rsc`)\n- React DevTools protocol\n- Behavioral 1:1 parity with React under concurrent-mode stress\n\nSee [docs\u002FSURFACE.md](.\u002Fdocs\u002FSURFACE.md) for the full React-19 export-by-export audit.\n\n---\n\n## Development\n\n### Layout\n\nOne package, one tree, internal subdirectories per concern:\n\n```\npackages\u002Fredact\u002Fsrc\u002F\n  core\u002F                     VDOM types + symbols (FiberTag, Hook, ReactNode, …)\n  react\u002F                    'react' entry: createElement, hooks, context, class,\n                            memo, suspense, jsx-runtime, ReactSharedInternals\n  dom\u002F                      'react-dom' entry: reconciler, host DOM, root,\n                            createPortal, flushSync\n    features\u002F               opt-in features (each is an index\u002Ffull\u002Fstub triple)\n      portal\u002F  context\u002F  suspense\u002F  memo\u002F\n      forward-ref\u002F  lazy\u002F  class\u002F  hydration\u002F\n  server\u002F                   'react-dom\u002Fserver' entry: renderToString,\n                            renderToReadableStream, renderToPipeableStream\n  scheduler\u002F                'scheduler' shim (no-op microtask wrapper)\n  vite\u002F                     redact() Vite plugin: aliases + feature-flag swaps\ntests\u002F                      vitest suite — 707 tests\nexamples\u002F\n  ssr-demo\u002F                 full SSR + Suspense streaming smoke app\ndocs\u002F\n  SURFACE.md                React 19 export audit\n  SAVINGS_ANALYSIS.md       per-export size savings vs React 19\nscripts\u002F\n  build.mjs                 per-entry esbuild build (every TS module emitted)\n  size.mjs                  per-preset \u002F per-flag gzip report\n  size-check.mjs            CI size-budget assertions\n  size-analyze.mjs          per-module byte breakdown for a given preset\n```\n\nCross-subdir imports inside `packages\u002Fredact\u002Fsrc\u002F` use relative paths\n(`..\u002Fcore`, `..\u002Freact`, etc.). The build emits each TS module as its own\ndist file with all relative imports kept literal — that's what preserves the\nimport-graph boundaries the Vite plugin needs to swap features at consumer\nbuild time.\n\n### Commands\n\n```bash\npnpm install\npnpm build                    # esbuild dist\u002F + tsc declaration emit\npnpm test                     # vitest suite (707 tests)\npnpm test:types               # tsc --noEmit\npnpm size                     # gzip\u002Fbrotli per entry + per feature-stub\npnpm size:check               # CI budget assertions (fails on regression)\npnpm --filter ssr-demo dev    # serve http:\u002F\u002Flocalhost:5173\n```\n\n### Current sizes\n\nSubpath sizes from `pnpm size`. The `react` \u002F `react-dom\u002Fclient` \u002F `react-dom\u002Fserver` column names are the user-facing aliases the Vite plugin sets up; under the hood they all resolve into `@tanstack\u002Fredact\u002F*`.\n\n| Entry | min | gzip | brotli |\n|---|---:|---:|---:|\n| `react`              (= `@tanstack\u002Fredact`)             | 6.59 KB | 2.65 KB | 2.41 KB |\n| `react\u002Fjsx-runtime`  (= `@tanstack\u002Fredact\u002Fjsx-runtime`) | 247 B | 189 B | 178 B |\n| `react-dom\u002Fclient`   (= `@tanstack\u002Fredact\u002Fdom-client`, `full`) | 26.56 KB | **9.07 KB** | 8.21 KB |\n| `react-dom\u002Fclient`   (= `@tanstack\u002Fredact\u002Fdom-client`, `nano`) | 18.75 KB | **6.75 KB** | 6.10 KB |\n| `react-dom\u002Fserver`   (= `@tanstack\u002Fredact\u002Fserver`)      | 11.48 KB | 4.59 KB | 4.16 KB |\n| **Client total** (`full`: react + react-dom\u002Fclient + jsx-runtime) | 32.63 KB | **11.18 KB** | 10.14 KB |\n\nRegenerate with `pnpm size`.\n\n---\n\n## Changelog\n\nThe project's first 9 alpha versions shipped as separate `@tanstack\u002Freact`, `@tanstack\u002Freact-dom`, `@tanstack\u002Freact-dom-server`, `@tanstack\u002Fdom-core`, `@tanstack\u002Fscheduler`, and `@tanstack\u002Fdom-vite` packages (`0.1.0-alpha.0` … `0.1.0-alpha.9`). Those packages are now deprecated. The project starts fresh as a single `@tanstack\u002Fredact` (`0.0.1`+) with subpath exports — the fixes below predate the rename and the package names refer to the previous multi-package layout.\n\n- `@tanstack\u002Fredact@0.0.1` — **first release of `@tanstack\u002Fredact`**. Consolidates the 6 previously-separate alpha packages into a single package with subpath exports (`.\u002Fjsx-runtime`, `.\u002Fdom`, `.\u002Fdom-client`, `.\u002Fdom-test-utils`, `.\u002Fserver`, `.\u002Fscheduler`, `.\u002Fvite`, `.\u002Ffeatures\u002F*`, `.\u002F_all`). Vite plugin renamed `tanstackDom()` → `redact()`, types `TanStackDom*` → `Redact*`. `ReactSharedInternals` made a `globalThis`-stashed singleton via `Symbol.for` to defend against duplicate package copies under bundlers like Cloudflare's `vite-plugin` that mix `noExternal: true` worker bundling with separate pre-bundled dep copies. New `tests\u002Fpublic-exports.test.ts` snapshot guards every subpath's named-export set against silent link-time drift.\n- `react@0.1.0-alpha.8` — added `useEffectEvent` hook (stable callback over a `useInsertionEffect`-refreshed ref). Fixes missing-export errors in consumers using React 19 event handlers.\n- `react-dom@0.1.0-alpha.8` — **feature-flag system landed**: 8 opt-in features with stub\u002Ffull pairs, typed Vite plugin config, `pnpm size:check` CI budget enforcement. `nano` preset ships **6.75 KB gzip** — a 26% reduction from `full`.\n- `react-dom@0.1.0-alpha.5` — `useEffect` \u002F `useLayoutEffect` cleanup now runs at effect-run time (in the passive drain) instead of dispatch time. Coalesced renders landing back-to-back before the drain (common with router\u002Fstore state updates triggered by one user action) no longer leak side-effects into the DOM.\n- `react-dom@0.1.0-alpha.4` — `renderFunction`'s deferred-hydration branch now matches `renderLazy`'s ancestor-Suspense guard (`_awaitingLazyHydration`). Fixes duplicate markup on RSC-hydrated subtrees.\n- `react-dom-server@0.1.0-alpha.4` — shell + bootstrap emits are buffered into one `TextEncoder.encode` + `ReadableStream.enqueue` instead of per-chunk, cutting Node stream overhead in the SSR CPU profile.\n\u003C\u002Fcontent>\n","TanStack\u002Fredact 是一个与React 100% API兼容但实现更简单的替代方案，旨在减小打包体积并提升性能。该项目使用TypeScript编写，通过提供一个名为`@tanstack\u002Fredact`的单一包来替换标准的React、React DOM及其它相关模块，用户代码无需更改导入语句即可无缝切换。redact在全功能启用时仅9.07KB（gzip压缩后），而采用精简配置则可进一步缩小至6.75KB。它支持所有关键特性如SSR、Suspense和hydration，并通过了全部707个单元及集成测试。此项目适用于需要高性能且对应用加载速度有严格要求的Web开发场景，尤其是那些希望减少初始加载时间以改善用户体验的应用。","2026-06-11 03:32:27","CREATED_QUERY"]