react-freeze
Suspends re-renders of hidden React component subtrees while preserving their state and native views.
Repository Health
Technical Analysis
react-freeze is a small React library from Software Mansion that lets you pause rendering for parts of a component tree using the Suspense mechanism introduced in React 17. Instead of unmounting components you want to hide — such as screens sitting underneath the active one in a navigation stack — you wrap them in a <Freeze> component that throws a never-resolving thenable to suspend their render pass while keeping their DOM nodes or native views, state, and scroll position fully intact.
It was built to solve a specific problem in React Navigation for React Native: stack-based navigators keep previous screens mounted so back-navigation is instant, but those hidden screens still re-render on every state or store update even though nothing is visible. react-freeze integrates with react-native-screens to skip that wasted work, and it works equally well as a standalone primitive in web React apps for tab panels, off-screen carousel items, or any subtree that shouldn’t re-render while hidden.
What You Get
- A single
<Freeze>component with afreezeboolean prop and optionalplaceholderfallback - State and native view preservation for frozen subtrees — no unmount, no lost scroll position or input state
- Drop-in integration with react-native-screens and React Navigation via
enableFreeze(true) - A zero-runtime-dependency implementation (only a peer dependency on React 17+) built on a single throwing
Suspendercomponent
Common Use Cases
- Freezing inactive stack screens in React Navigation so they stop re-rendering while off-screen
- Pausing hidden tab or carousel panels in web apps that shouldn’t process store updates until visible
- Preventing background list sections or off-viewport content from re-rendering on every state change
- Keeping scroll position, form input, and loaded images unchanged when a view is temporarily hidden
Under The Hood
Architecture
The entire implementation lives in a single ~30-line file (src/index.tsx) that exports one component, Freeze. It composes a Suspense boundary around an internal Suspender component; when the freeze prop is true, Suspender throws a module-level, never-resolving thenable (infiniteThenable), which trips React’s Suspense mechanism and holds the subtree’s commit — rendering the placeholder fallback — without ever unmounting the underlying tree. There is no additional layering, state, or dependency injection: the whole behavior is one conditional throw wrapped in two nested components, which keeps the surface area (and the number of things that can go wrong) deliberately minimal.
Tech Stack
Written in TypeScript against a peer dependency on React 17+, with a parallel react-native entry point that points directly at the TypeScript source so React Native’s Metro bundler consumes it unbundled. It’s built with microbundle-crl into modern ESM and CJS bundles for regular React consumers, tested with the Create React App test runner (react-scripts test) on top of Jest and react-test-renderer, type-checked with tsc --noEmit, and linted with ESLint (typescript-eslint plus eslint-config-standard-react) and Prettier. It has no runtime dependencies beyond React itself.
Code Quality
A dedicated test file exercises the full freeze/defrost lifecycle — initial render, suspending on freeze, state changes not propagating while frozen, and state changes replaying correctly on defrost — using act() and react-test-renderer, which is thorough coverage for a library this size. Props are typed via explicit TypeScript interfaces, and CI (GitHub Actions) runs unit tests, lint, type-checking, and the build on every push and pull request to main, so regressions in any of those dimensions are caught automatically.
API Design
The public API is a single component with two props — a required freeze boolean and an optional placeholder node — so adopting it requires no configuration beyond wrapping existing JSX. Naming is self-descriptive, and the README pairs the quick-start snippet with an extensive FAQ that walks through edge cases (what happens to state updates while frozen, how scroll position and native views survive, what can break if you freeze the wrong subtree), which is unusually thorough documentation for a library this small.