One plugin. Same imports.
pnpm add @tanstack/redactAdd redact() to your existing Vite plugins, keep your framework and React plugins in place. The plugin aliases React, React DOM, server rendering, scheduler, and JSX entrypoints for client and SSR builds.
// Your application code stays familiar.
import { useState } from 'react'
import { createRoot } from 'react-dom/client'
function Counter() {
const [count, setCount] = useState(0)
return (
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
)
}
createRoot(document.getElementById('root')!).render(<Counter />)Redact is a React-compatible runtime, not a complete implementation of every React behavior. Check the compatibility differences below before switching an existing app.
Less runtime to download.
| Runtime | Gzip size |
|---|---|
| React 19.3.0 | 69,162 bytes |
| Redact, default Vite configuration | 23,311 bytes |
| Redact, native animation enabled | 28,159 bytes |
The default bundle was 66.3% smaller in the Redact 0.1.0 release measurement from September 11, 2026. These are runtime sizes, your app will tree-shake differently, and the runtimes have different scheduling capabilities. Measurement inputs and configurations ↗
Measured separately in the same release, the default DOM-client entry is 20,139 bytes gzipped and the nano DOM-client entry is 12,466 bytes. The React API entry is 2,764 bytes. These entries overlap, their sizes are not additive. Nano disables all optional features and is not a full-compatibility preset. Choose your feature flags.
Performance depends on the workload. Published microbenchmarks include faster state updates and slower hydration and Suspense retries. They do not establish whole-app speedups. Read the performance measurements ↗
Know what changes.
Redact keeps the React component model and everyday APIs, while leaving out concurrent scheduling. These behaviors describe the default Vite configuration.
| Area | Redact behavior |
|---|---|
| Components and hooks | JSX, state, effects, context, refs, memo, lazy, portals, error boundaries, and modern class lifecycles are supported. Legacy class lifecycles are no-ops. |
| Suspense and hydration | Fallbacks, retries, retained primary DOM, streaming boundaries, and event replay are supported. Work runs without priority scheduling. |
| Transitions | startTransition and useTransition run synchronously. Pending stays false. useDeferredValue returns its input. There is no time slicing or interruptible rendering. |
| Actions and optimistic state | useActionState returns the initial state, a no-op dispatch, and false. useOptimistic has no optimistic overlay and its setter is a no-op. useFormStatus stays idle. |
| Activity | Hidden DOM and state are retained. Effects, refs, and subscriptions disconnect, insertion effects stay connected. Hidden work runs synchronously. |
| Server rendering | String, static markup, readable stream, and pipeable stream rendering are supported. Full Fizz prerender and resume APIs are not implemented. |
| Server Components | The Vite RSC environment stays on upstream React. Redact does not reimplement Flight serialization. |
| Caching | cache returns its function and cacheSignal returns null. Client and DOM-server rendering are uncached. |
| Development tools | Fast Refresh works with @vitejs/plugin-react. StrictMode and Profiler render children without double invocation or profiling. React DevTools inspection is not implemented. |
| React Compiler | The react/compiler-runtime memo-cache entrypoint is implemented and aliased. Every compiler output pattern has not been validated. |
Native view transition animation is experimental and opt-in. Production animation, hidden-tab behavior, broader resource and navigation races, and Safari and Firefox remain unverified. Full API support reference ↗
Only ship the parts you use.
The default full preset enables every optional feature except native animation. Disable behavior your app does not use, or start with nano and turn features on individually. Overrides merge with the preset.
// A client-rendered app without class components
redact({
features: { hydration: false, classComponents: false },
})
// Every optional feature off, then context enabled
redact({ preset: 'nano', features: { context: true } })
// Experimental native view transition animation
redact({ features: { viewTransitions: true } })Disabling a feature changes behavior, it does not just remove an unused export. The nano preset is not a full-compatibility preset. Feature size savings overlap and are not additive.
| Feature flag | When disabled |
|---|---|
activity | Hidden children unmount and lose state. |
fragmentRefs | No Fragment DOM instance. |
viewTransitions | No native snapshots or animation callbacks, the boundary retains state. |
portal | Children render in place instead of into the target container. |
context | Providers do not propagate values, reads return the default. |
suspense | No fallback UI, thrown thenables still retry when they settle. |
memo | No prop-equality bailout. |
forwardRef | No wrapper forwarding, ordinary ref props still work. |
lazy | Only already-synchronous payloads resolve. |
classComponents | Constructor, render, and setState only, no lifecycles or error boundaries. |
hydration | hydrateRoot throws, use createRoot. |
Vite plugin reference
import { redact } from '@tanstack/redact/vite'
redact({
preset: 'full',
features: { viewTransitions: false },
})| Option | Purpose |
|---|---|
preset | 'full' | 'nano', defaults to 'full'. |
features | Boolean overrides for the feature flags above. |
skip | An array of import specifiers to leave out of aliasing. |
resolveFrom | Package resolution root, defaults to the Vite config root. Useful in monorepos. |
packageRoots | A map of package names to absolute package directories, bypassing node_modules lookup for local builds. |
For runtime internals and manual configuration, see the development guide ↗.
Try it with your app.
Run your tests and production build, then check hydration, forms, portals, navigation, and any third-party components your app uses. Pay particular attention to code that depends on pending transitions, actions, optimistic state, or React DevTools.
The repository publishes browser test results, real-app integration checks, and benchmark reproduction instructions. Use those alongside your own app's checks when deciding whether to switch.