-
Notifications
You must be signed in to change notification settings - Fork 22
Description
Describe the bug
There is a memory leak in VariableContextProvider that causes memory to grow continuously with each component re-render. The memory is never released even after garbage collection, and the leak scales with the number of CSS variables passed to the provider.
Reproduction
Repository: https://github.com/alias-mac/nativewind-mem-leak
To reproduce:
- Clone the reproduction repository
- Run
npm installand start the app withnpm run iosornpm run android - Open your memory profiler (Xcode Instruments, Android Studio Profiler, or React DevTools)
- Click the "Clicked X times" button repeatedly (50-100 times)
- Observe memory usage continuously growing without being released
The reproduction includes:
- A
ThemeProvidercomponent usingVariableContextProviderwith 1000 CSS variables (these are just examples) - A simple counter component that re-renders on each click
- Components using
classNamewith the CSS variables (e.g.,text-primary) - The leak occurs regardless of animations - tested with and without
animate-pulse
Key files:
/components/ThemeProvider.tsx- Shows VariableContextProvider with many CSS variables/app/index.tsx- Simple re-rendering component withkey={count}to force remounts/global.css- CSS variable definition with--color-primary: var(--foo-primary)
Expected behavior
Memory should remain stable or grow minimally with re-renders. After garbage collection, memory should return to baseline levels. CSS variable context should be cleaned up when components unmount or update.
Additional context
- Using NativeWind v5 (preview) with Tailwind CSS v4
- The leak appears to be related to how
VariableContextProvidermanages CSS variable context internally - The severity increases with the number of CSS variables passed to the provider (tested with 1000 variables and leaking 20.5kB on each re-render, but our app has about 40kB for this)
- Each re-render appears to create new references that are not properly garbage collected
- Adding
key={count}to force component remounting makes the leak more pronounced
After only 4 "clicks/re-renders" it increased the size of the DictPropertyMap by 38:
We were able to validate that this issue is reproducible in the production version of the app, so it isn't based on the dev mode (with HMR, dev-client or other developer tools enabled).