nativewind icon indicating copy to clipboard operation
nativewind copied to clipboard

Memory leak in `VariableContextProvider` when re-rendering with CSS variables

Open alias-mac opened this issue 5 months ago • 0 comments

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:

  1. Clone the reproduction repository
  2. Run npm install and start the app with npm run ios or npm run android
  3. Open your memory profiler (Xcode Instruments, Android Studio Profiler, or React DevTools)
  4. Click the "Clicked X times" button repeatedly (50-100 times)
  5. Observe memory usage continuously growing without being released

The reproduction includes:

  • A ThemeProvider component using VariableContextProvider with 1000 CSS variables (these are just examples)
  • A simple counter component that re-renders on each click
  • Components using className with 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 with key={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 VariableContextProvider manages 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
Image

After only 4 "clicks/re-renders" it increased the size of the DictPropertyMap by 38:

Image

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).

alias-mac avatar Nov 05 '25 20:11 alias-mac